diff --git a/.github/ISSUE_TEMPLATE/question-about-using-erpnext.md b/.github/ISSUE_TEMPLATE/question-about-using-erpnext.md index 455c20ebda4..2016bcc6a65 100644 --- a/.github/ISSUE_TEMPLATE/question-about-using-erpnext.md +++ b/.github/ISSUE_TEMPLATE/question-about-using-erpnext.md @@ -8,7 +8,7 @@ Please post on our forums: for questions about using `ERPNext`: https://discuss.erpnext.com -for questions about using the `Frappe Framework`: https://discuss.frappe.io +for questions about using the `Frappe Framework`: ~~https://discuss.frappe.io~~ => [stackoverflow](https://stackoverflow.com/questions/tagged/frappe) tagged under `frappe` for questions about using `bench`, probably the best place to start is the [bench repo](https://github.com/frappe/bench) diff --git a/.snyk b/.snyk new file mode 100644 index 00000000000..140f3edd846 --- /dev/null +++ b/.snyk @@ -0,0 +1,8 @@ +# Snyk (https://snyk.io) policy file, patches or ignores known vulnerabilities. +version: v1.14.0 +ignore: {} +# patches apply the minimum changes required to fix a vulnerability +patch: + SNYK-JS-LODASH-450202: + - cypress > getos > async > lodash: + patched: '2020-01-31T01:35:12.802Z' diff --git a/.travis.yml b/.travis.yml index 40afeee8d46..365eb67f3dc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,6 +63,7 @@ install: - tar -xf /tmp/wkhtmltox.tar.xz -C /tmp - sudo mv /tmp/wkhtmltox/bin/wkhtmltopdf /usr/local/bin/wkhtmltopdf - sudo chmod o+x /usr/local/bin/wkhtmltopdf + - sudo apt-get install libcups2-dev - cd ~/frappe-bench diff --git a/README.md b/README.md index 64f8d67d8e8..ed57a17279c 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,26 @@ -Includes: Accounting, Inventory, Manufacturing, CRM, Sales, Purchase, Project Management, HRMS. Requires MariaDB. +ERPNext as a monolith includes the following areas for managing businesses: -ERPNext is built on the [Frappe](https://github.com/frappe/frappe) Framework, a full-stack web app framework in Python & JavaScript. +1. [Accounting](https://erpnext.com/docs/user/manual/en/accounts) +1. [Inventory](https://erpnext.com/docs/user/manual/en/stock) +1. [CRM](https://erpnext.com/docs/user/manual/en/CRM) +1. [Sales](https://erpnext.com/docs/user/manual/en/selling) +1. [Purchase](https://erpnext.com/docs/user/manual/en/buying) +1. [HRMS](https://erpnext.com/docs/user/manual/en/human-resources) +1. [Project Management](https://erpnext.com/docs/user/manual/en/projects) +1. [Support](https://erpnext.com/docs/user/manual/en/support) +1. [Asset Management](https://erpnext.com/docs/user/manual/en/asset) +1. [Quality Management](https://erpnext.com/docs/user/manual/en/quality-management) +1. [Manufacturing](https://erpnext.com/docs/user/manual/en/manufacturing) +1. [Website Management](https://erpnext.com/docs/user/manual/en/website) +1. [Customize ERPNext](https://erpnext.com/docs/user/manual/en/customize-erpnext) +1. [And More](https://erpnext.com/docs/user/manual/en/) + +ERPNext requires MariaDB. + +ERPNext is built on the [Frappe Framework](https://github.com/frappe/frappe), a full-stack web app framework built with Python & JavaScript. - [User Guide](https://erpnext.com/docs/user) - [Discussion Forum](https://discuss.erpnext.com/) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index d031bc5bb17..f40b9575632 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '12.1.8' +__version__ = '12.2.0' def get_default_company(user=None): '''Get default company for user''' diff --git a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py index 716bef381b3..43acded3a98 100644 --- a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py +++ b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.py @@ -93,7 +93,8 @@ def get_gl_entries(account, to_date): fields = ['posting_date', 'debit', 'credit'], filters = [ dict(posting_date = ('<', to_date)), - dict(account = ('in', child_accounts)) + dict(account = ('in', child_accounts)), + dict(voucher_type = ('!=', 'Period Closing Voucher')) ], order_by = 'posting_date asc') diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py index 62a8f05c659..3b6a5881ca0 100644 --- a/erpnext/accounts/deferred_revenue.py +++ b/erpnext/accounts/deferred_revenue.py @@ -30,7 +30,7 @@ def validate_service_stop_date(doc): frappe.throw(_("Service Stop Date cannot be after Service End Date")) if old_stop_dates and old_stop_dates.get(item.name) and item.service_stop_date!=old_stop_dates.get(item.name): - frappe.throw(_("Cannot change Service Stop Date for item in row {0}".format(item.idx))) + frappe.throw(_("Cannot change Service Stop Date for item in row {0}").format(item.idx)) def convert_deferred_expense_to_expense(start_date=None, end_date=None): # book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index cccced8e0be..1407d5f5fe3 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -95,28 +95,29 @@ class Account(NestedSet): # ignore validation while creating new compnay or while syncing to child companies if frappe.local.flags.ignore_root_company_validation or self.flags.ignore_root_company_validation: return - ancestors = get_root_company(self.company) if ancestors: if frappe.get_value("Company", self.company, "allow_account_creation_against_child_company"): return - if not frappe.db.get_value("Account", {'account_name': self.account_name, 'company': ancestors[0]}, 'name'): frappe.throw(_("Please add the account to root level Company - %s" % ancestors[0])) else: descendants = get_descendants_of('Company', self.company) if not descendants: return - parent_acc_name_map = {} - parent_acc_name = frappe.db.get_value('Account', self.parent_account, "account_name") - for d in frappe.db.get_values('Account', - {"company": ["in", descendants], "account_name": parent_acc_name}, - ["company", "name"], as_dict=True): + parent_acc_name, parent_acc_number = frappe.db.get_value('Account', self.parent_account, \ + ["account_name", "account_number"]) + filters = { + "company": ["in", descendants], + "account_name": parent_acc_name, + } + if parent_acc_number: + filters["account_number"] = parent_acc_number + + for d in frappe.db.get_values('Account', filters=filters, fieldname=["company", "name"], as_dict=True): parent_acc_name_map[d["company"]] = d["name"] - if not parent_acc_name_map: return - self.create_account_for_child_company(parent_acc_name_map, descendants, parent_acc_name) def validate_group_or_ledger(self): @@ -174,7 +175,6 @@ class Account(NestedSet): filters["account_number"] = self.account_number child_account = frappe.db.get_value("Account", filters, 'name') - if not child_account: doc = frappe.copy_doc(self) doc.flags.ignore_root_company_validation = True diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/ae_uae_chart_template_standard.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/ae_uae_chart_template_standard.json index 8856c8cc90f..a8afb55df6f 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/ae_uae_chart_template_standard.json +++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/ae_uae_chart_template_standard.json @@ -1,465 +1,466 @@ { - "country_code": "ae", - "name": "U.A.E - Chart of Accounts", + "country_code": "ae", + "name": "U.A.E - Chart of Accounts", "tree": { "Assets": { "Current Assets": { "Accounts Receivable": { "Corporate Credit Cards": { "account_type": "Receivable" - }, + }, "Other Receivable": { "Accrued Rebates Due from Suppliers": { "account_type": "Receivable" - }, + }, "Accrued Income from Suppliers": { "account_type": "Receivable" - }, + }, "Other Debtors": { "account_type": "Receivable" - }, + }, "account_type": "Receivable" - }, + }, "Post Dated Cheques Received": { "account_type": "Receivable" - }, + }, "Staff Receivable": { "account_type": "Receivable" - }, + }, "Trade Receivable": { "account_type": "Receivable" - }, + }, "Trade in Opening Fees": { "account_type": "Receivable" - }, + }, "account_type": "Receivable" - }, + }, "Cash in Hand & Banks": { "Banks": { - "Bank Margin On LC & LG": {}, - "Banks Blocked Deposits": {}, - "Banks Call Deposit Accounts": {}, + "Bank Margin On LC & LG": {}, + "Banks Blocked Deposits": {}, + "Banks Call Deposit Accounts": {}, "Banks Current Accounts": { "account_type": "Bank" - }, + }, "account_type": "Bank" - }, + }, "Cash in Hand": { "Cash in Safe": { "Main Safe": { "account_type": "Cash" - }, + }, "Main Safe - Foreign Currency": { "account_type": "Cash" } - }, + }, "Petty Cash": { "Petty Cash - Administration": { "account_type": "Cash" - }, + }, "Petty Cash - Others": { "account_type": "Cash" } - }, + }, "account_type": "Cash" - }, + }, "Cash in Transit": { "Credit Cards": { "Gateway Credit Cards": { "account_type": "Bank" - }, + }, "Manual Visa & Master Cards": { "account_type": "Bank" - }, + }, "PayPal Account": { "account_type": "Bank" - }, + }, "Visa & Master Credit Cards": { "account_type": "Bank" } } } - }, + }, "Inventory": { "Consigned Stock": { - "Handling Difference in Inventory": { - "account_type": "Stock Adjustment" - }, + "Handling Difference in Inventory": {}, "Items Delivered to Customs on temporary Base": {} - }, + }, "Stock in Hand": { "account_type": "Stock" } - }, + }, "Preliminary and Preoperating Expenses": { "Preoperating Expenses": {} - }, + }, "Prepayments & Deposits": { "Deposits": { - "Deposit - Office Rent": {}, - "Deposit Others": {}, - "Deposit to Immigration (Visa)": {}, + "Deposit - Office Rent": {}, + "Deposit Others": {}, + "Deposit to Immigration (Visa)": {}, "Deposits - Customs": {} - }, + }, "Prepaid Taxes": { - "Sales Taxes Receivables": {}, + "Sales Taxes Receivables": {}, "Withholding Tax Receivables": {} - }, + }, "Prepayments": { - "Other Prepayments": {}, - "PrePaid Advertisement Expenses": {}, - "Prepaid Bank Guarantee": {}, - "Prepaid Consultancy Fees": {}, - "Prepaid Employees Housing": {}, - "Prepaid Finance charge for Loans": {}, - "Prepaid Legal Fees": {}, - "Prepaid License Fees": {}, - "Prepaid Life Insurance": {}, - "Prepaid Maintenance": {}, - "Prepaid Medical Insurance": {}, - "Prepaid Office Rent": {}, - "Prepaid Other Insurance": {}, - "Prepaid Schooling Fees": {}, - "Prepaid Site Hosting Fees": {}, + "Other Prepayments": {}, + "PrePaid Advertisement Expenses": {}, + "Prepaid Bank Guarantee": {}, + "Prepaid Consultancy Fees": {}, + "Prepaid Employees Housing": {}, + "Prepaid Finance charge for Loans": {}, + "Prepaid Legal Fees": {}, + "Prepaid License Fees": {}, + "Prepaid Life Insurance": {}, + "Prepaid Maintenance": {}, + "Prepaid Medical Insurance": {}, + "Prepaid Office Rent": {}, + "Prepaid Other Insurance": {}, + "Prepaid Schooling Fees": {}, + "Prepaid Site Hosting Fees": {}, "Prepaid Sponsorship Fees": {} } } - }, + }, "Long Term Assets": { "Fixed Assets": { "Accumulated Depreciation": { "Acc. Depreciation of Motor Vehicles": { "account_type": "Accumulated Depreciation" - }, + }, "Acc. Deprn.Computer Hardware & Software": { "account_type": "Accumulated Depreciation" - }, + }, "Acc.Deprn.of Furniture & Office Equipment": { "account_type": "Accumulated Depreciation" - }, + }, "Amortisation on Leasehold Improvement": { "account_type": "Accumulated Depreciation" - }, + }, "account_type": "Accumulated Depreciation" - }, + }, "Fixed Assets (Cost Price)": { "Computer Hardware & Software": { "account_type": "Fixed Asset" - }, + }, "Furniture and Equipment": { "account_type": "Fixed Asset" - }, - "Leasehold Improvement": {}, + }, + "Leasehold Improvement": {}, "Motor Vehicles": { "account_type": "Fixed Asset" - }, - "Work In Progress": {}, + }, + "Work In Progress": {}, "account_type": "Fixed Asset" } - }, + }, "Intangible Assets": { - "Computer Card Renewal": {}, - "Disposal of Outlets": {}, + "Computer Card Renewal": {}, + "Disposal of Outlets": {}, "Registration of Trademarks": {} - }, - "Intercompany Accounts": {}, + }, + "Intercompany Accounts": {}, "Investments": { "Investments in Subsidiaries": {} } - }, + }, "root_type": "Asset" - }, + }, "Closing And Temporary Accounts": { "Closing Accounts": { "Closing Account": {} - }, + }, "root_type": "Liability" - }, + }, "Expenses": { "Commercial Expenses": { - "Consultancy Fees": {}, + "Consultancy Fees": {}, "Provision for Doubtful Debts": {} - }, + }, "Cost of Sale": { "Cost Of Goods Sold": { - "Cost Of Goods Sold I/C Sales": {}, + "Cost Of Goods Sold I/C Sales": {}, "Cost of Goods Sold in Trading": { "account_type": "Cost of Goods Sold" - }, + }, "account_type": "Cost of Goods Sold" - }, + }, "Expenses Included In Valuation": { "account_type": "Expenses Included In Valuation" + }, + "Stock Adjustment": { + "account_type": "Stock Adjustment" } - }, + }, "Depreciation": { "Depreciation & Amortization": { - "Amortization on Leasehold Improvement": {}, + "Amortization on Leasehold Improvement": {}, "Depreciation Of Computer Hard & Soft": { "account_type": "Depreciation" - }, + }, "Depreciation Of Furniture & Office Equipment\n\t\t\t": { "account_type": "Depreciation" - }, + }, "Depreciation Of Motor Vehicles": { "account_type": "Depreciation" } } - }, + }, "Direct Expenses": { "Financial Charges": { - "Air Miles Card Charges": {}, - "Amex Credit Cards Charges": {}, - "Bank Finance & Loan Charges": {}, - "Credit Card Charges": {}, - "Credit Card Swipe Charges": {}, + "Air Miles Card Charges": {}, + "Amex Credit Cards Charges": {}, + "Bank Finance & Loan Charges": {}, + "Credit Card Charges": {}, + "Credit Card Swipe Charges": {}, "PayPal Charges": {} } - }, + }, "MISC Charges": { "Other Charges": { "Capital Loss": { - "Disposal of Business Branch": {}, - "Loss On Fixed Assets Disposal": {}, + "Disposal of Business Branch": {}, + "Loss On Fixed Assets Disposal": {}, "Loss on Difference on Exchange": {} - }, + }, "Other Non Operating Exp": { "Other Non Operating Expenses": {} - }, + }, "Previous Year Adjustments": { "Previous Year Adjustments Account": {} - }, + }, "Royalty Fees": { "Royalty to Parent Co.": {} - }, + }, "Tax / Zakat Expenses": { "Income Tax": { "account_type": "Tax" - }, - "Zakat": {}, + }, + "Zakat": {}, "account_type": "Tax" } } - }, + }, "Share Resources": { "Share Resource Expenses Account": {} - }, + }, "Store Operating Expenses": { "Selling, General & Admin Expenses": { "Advertising Expenses": { "Other - Advertising Expenses": {} - }, + }, "Bank & Finance Charges": { "Other Bank Charges": {} - }, + }, "Communications": { - "Courier": {}, - "Others - Communication": {}, - "Telephone": {}, + "Courier": {}, + "Others - Communication": {}, + "Telephone": {}, "Web Site Hosting Fees": {} - }, + }, "Office & Various Expenses": { - "Cleaning": {}, - "Conveyance Expenses": {}, - "Gifts & Donations": {}, - "Insurance": {}, - "Kitchen and Buffet Expenses": {}, - "Maintenance": {}, - "Others - Office Various Expenses": {}, - "Security & Guard": {}, - "Stationary From Suppliers": {}, - "Stationary Out Of Stock": {}, - "Subscriptions": {}, - "Training": {}, + "Cleaning": {}, + "Conveyance Expenses": {}, + "Gifts & Donations": {}, + "Insurance": {}, + "Kitchen and Buffet Expenses": {}, + "Maintenance": {}, + "Others - Office Various Expenses": {}, + "Security & Guard": {}, + "Stationary From Suppliers": {}, + "Stationary Out Of Stock": {}, + "Subscriptions": {}, + "Training": {}, "Vehicle Expenses": {} - }, + }, "Personnel Cost": { - "Basic Salary": {}, - "End Of Service Indemnity": {}, - "Housing Allowance": {}, - "Leave Salary": {}, - "Leave Ticket": {}, - "Life Insurance": {}, - "Medical Insurance": {}, - "Personnel Cost Others": {}, - "Sales Commission": {}, - "Staff School Allowances": {}, - "Transportation Allowance": {}, - "Uniform": {}, + "Basic Salary": {}, + "End Of Service Indemnity": {}, + "Housing Allowance": {}, + "Leave Salary": {}, + "Leave Ticket": {}, + "Life Insurance": {}, + "Medical Insurance": {}, + "Personnel Cost Others": {}, + "Sales Commission": {}, + "Staff School Allowances": {}, + "Transportation Allowance": {}, + "Uniform": {}, "Visa Expenses": {} - }, + }, "Professional & Legal Fees": { - "Audit Fees": {}, - "Legal fees": {}, - "Others - Professional Fees": {}, - "Sponsorship Fees": {}, + "Audit Fees": {}, + "Legal fees": {}, + "Others - Professional Fees": {}, + "Sponsorship Fees": {}, "Trade License Fees": {} - }, + }, "Provision & Write Off": { - "Amortisation of Preoperating Expenses": {}, - "Cash Shortage": {}, - "Others - Provision & Write off": {}, - "Write Off Inventory": {}, + "Amortisation of Preoperating Expenses": {}, + "Cash Shortage": {}, + "Others - Provision & Write off": {}, + "Write Off Inventory": {}, "Write Off Receivables & Payables": {} - }, + }, "Rent Expenses": { - "Office Rent": {}, + "Office Rent": {}, "Warehouse Rent": {} - }, + }, "Travel Expenses": { - "Air tickets": {}, - "Hotel": {}, - "Meals": {}, - "Others": {}, + "Air tickets": {}, + "Hotel": {}, + "Meals": {}, + "Others": {}, "Per Diem": {} - }, + }, "Utilities": { - "Other Utility Cahrges": {}, + "Other Utility Cahrges": {}, "Water & Electricity": {} } } - }, + }, "root_type": "Expense" - }, + }, "Liabilities": { "Current Liabilities": { "Accounts Payable": { "Payables": { "Advance Payable to Suppliers": { "account_type": "Payable" - }, + }, "Consigned Payable": { "account_type": "Payable" - }, + }, "Other Payable": { "account_type": "Payable" - }, + }, "Post Dated Cheques Paid": { "account_type": "Payable" - }, - "Staff Payable": {}, + }, + "Staff Payable": {}, "Suppliers Price Protection": { "account_type": "Payable" - }, + }, "Trade Payable": { "account_type": "Payable" - }, + }, "account_type": "Payable" } - }, + }, "Accruals & Provisions": { "Accruals": { "Accrued Personnel Cost": { - "Accrued - Commissions": {}, - "Accrued - Leave Salary": {}, - "Accrued - Leave Tickets": {}, - "Accrued - Salaries": {}, - "Accrued Other Personnel Cost": {}, - "Accrued Salaries Increment": {}, + "Accrued - Commissions": {}, + "Accrued - Leave Salary": {}, + "Accrued - Leave Tickets": {}, + "Accrued - Salaries": {}, + "Accrued Other Personnel Cost": {}, + "Accrued Salaries Increment": {}, "Accrued-Staff Bonus": {} } - }, + }, "Accrued Expenses": { "Accrued Other Expenses": { - "Accrued - Audit Fees": {}, - "Accrued - Office Rent": {}, - "Accrued - Sponsorship": {}, - "Accrued - Telephone": {}, - "Accrued - Utilities": {}, + "Accrued - Audit Fees": {}, + "Accrued - Office Rent": {}, + "Accrued - Sponsorship": {}, + "Accrued - Telephone": {}, + "Accrued - Utilities": {}, "Accrued Others": {} } - }, + }, "Other Current Liabilities": { - "Accrued Dubai Customs": {}, - "Deferred income": {}, + "Accrued Dubai Customs": {}, + "Deferred income": {}, "Shipping & Handling": {} - }, + }, "Provisions": { "Tax Payables": { - "Income Tax Payable": {}, - "Sales Tax Payable": {}, + "Income Tax Payable": {}, + "Sales Tax Payable": {}, "Withholding Tax Payable": {} } - }, + }, "Short Term Loan": {} - }, + }, "Duties and Taxes": { - "account_type": "Tax", + "account_type": "Tax", "is_group": 1 - }, + }, "Reservations & Credit Notes": { "Credit Notes": { - "Credit Notes to Customers": {}, + "Credit Notes to Customers": {}, "Reservations": {} } - }, + }, "Stock Liabilities": { "Stock Received But Not Billed": { "account_type": "Stock Received But Not Billed" } - }, + }, "Unearned Income": {} - }, + }, "Long Term Liabilities": { "Long Term Loans & Provisions": {} - }, + }, "root_type": "Liability" - }, + }, "Revenue": { "Direct Revenue": { "Other Direct Revenue": { "Other Revenue - Operating": { - "Advertising Income": {}, - "Branding Income": {}, - "Early Setmt Margin from Suppliers": {}, - "Marketing Rebate from Suppliers": {}, - "Rebate from Suppliers": {}, - "Service Income": {}, + "Advertising Income": {}, + "Branding Income": {}, + "Early Setmt Margin from Suppliers": {}, + "Marketing Rebate from Suppliers": {}, + "Rebate from Suppliers": {}, + "Service Income": {}, "Space Rental Income": {} } } - }, + }, "Indirect Revenue": { "Other Indirect Revenue": { - "Capital Gain": {}, - "Excess In Till": {}, - "Gain On Difference Of Exchange": {}, - "Management Consultancy Fees": {}, + "Capital Gain": {}, + "Excess In Till": {}, + "Gain On Difference Of Exchange": {}, + "Management Consultancy Fees": {}, "Other Income": {} - }, + }, "Other Revenue - Non Operating": { - "Interest Revenue": {}, - "Interest from FD": {}, - "Products Listing Fees from Suppliers": {}, + "Interest Revenue": {}, + "Interest from FD": {}, + "Products Listing Fees from Suppliers": {}, "Trade Opening Fees from suppliers": {} } - }, + }, "Sales": { "Sales from Other Regions": { "Sales from Other Region": {} - }, + }, "Sales of same region": { - "Management Consultancy Fees 1": {}, - "Sales Account": {}, + "Management Consultancy Fees 1": {}, + "Sales Account": {}, "Sales of I/C": {} } - }, + }, "root_type": "Income" - }, + }, "Share Holder Equity": { "Capital": { - "Contributed Capital": {}, - "Share Capital": {}, - "Shareholders Current A/c": {}, - "Sub Ordinated Loan": {}, + "Contributed Capital": {}, + "Share Capital": {}, + "Shareholders Current A/c": {}, + "Sub Ordinated Loan": {}, "Treasury Stocks": {} - }, + }, "Retained Earnings": { - "Current Year Results": {}, - "Dividends Paid": {}, + "Current Year Results": {}, + "Dividends Paid": {}, "Previous Years Results": {} - }, - "account_type": "Equity", + }, + "account_type": "Equity", "root_type": "Equity" } } diff --git a/erpnext/accounts/doctype/accounting_period/accounting_period.py b/erpnext/accounts/doctype/accounting_period/accounting_period.py index 180460c091c..f48d6dfc953 100644 --- a/erpnext/accounts/doctype/accounting_period/accounting_period.py +++ b/erpnext/accounts/doctype/accounting_period/accounting_period.py @@ -41,8 +41,8 @@ class AccountingPeriod(Document): def get_doctypes_for_closing(self): docs_for_closing = [] - doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", "Bank Reconciliation", - "Asset", "Purchase Order", "Sales Order", "Leave Application", "Leave Allocation", "Stock Entry"] + doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", \ + "Bank Reconciliation", "Asset", "Stock Entry"] closed_doctypes = [{"document_type": doctype, "closed": 1} for doctype in doctypes] for closed_doctype in closed_doctypes: docs_for_closing.append(closed_doctype) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py index 3222aeb0855..2473d715d0d 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -15,8 +15,8 @@ class AccountsSettings(Document): frappe.clear_cache() def validate(self): - for f in ["add_taxes_from_item_tax_template"]: - frappe.db.set_default(f, self.get(f, "")) + frappe.db.set_default("add_taxes_from_item_tax_template", + self.get("add_taxes_from_item_tax_template", 0)) self.validate_stale_days() self.enable_payment_schedule_in_print() diff --git a/erpnext/accounts/doctype/bank_account/bank_account.json b/erpnext/accounts/doctype/bank_account/bank_account.json index 8e30b8555cb..c8ae26d9f2c 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.json +++ b/erpnext/accounts/doctype/bank_account/bank_account.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "creation": "2017-05-29 21:35:13.136357", @@ -82,7 +83,7 @@ "default": "0", "fieldname": "is_default", "fieldtype": "Check", - "label": "Is the Default Account" + "label": "Is Default Account" }, { "default": "0", @@ -211,7 +212,8 @@ "read_only": 1 } ], - "modified": "2019-10-02 01:34:12.417601", + "links": [], + "modified": "2020-01-29 20:42:26.458316", "modified_by": "Administrator", "module": "Accounts", "name": "Bank Account", diff --git a/erpnext/accounts/doctype/bank_account/test_bank_account.py b/erpnext/accounts/doctype/bank_account/test_bank_account.py index f3bb086fa96..ed34d17ee74 100644 --- a/erpnext/accounts/doctype/bank_account/test_bank_account.py +++ b/erpnext/accounts/doctype/bank_account/test_bank_account.py @@ -31,7 +31,7 @@ class TestBankAccount(unittest.TestCase): try: bank_account.validate_iban() except AttributeError: - msg = _('BankAccount.validate_iban() failed for empty IBAN') + msg = 'BankAccount.validate_iban() failed for empty IBAN' self.fail(msg=msg) for iban in valid_ibans: @@ -39,11 +39,11 @@ class TestBankAccount(unittest.TestCase): try: bank_account.validate_iban() except ValidationError: - msg = _('BankAccount.validate_iban() failed for valid IBAN {}'.format(iban)) + msg = 'BankAccount.validate_iban() failed for valid IBAN {}'.format(iban) self.fail(msg=msg) for not_iban in invalid_ibans: bank_account.iban = not_iban - msg = _('BankAccount.validate_iban() accepted invalid IBAN {}'.format(not_iban)) + msg = 'BankAccount.validate_iban() accepted invalid IBAN {}'.format(not_iban) with self.assertRaises(ValidationError, msg=msg): bank_account.validate_iban() diff --git a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py b/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py index 1318cf18d76..5b6eb9dc246 100644 --- a/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py +++ b/erpnext/accounts/doctype/bank_statement_transaction_entry/bank_statement_transaction_entry.py @@ -314,7 +314,7 @@ class BankStatementTransactionEntry(Document): try: reconcile_against_document(lst) except: - frappe.throw(_("Exception occurred while reconciling {0}".format(payment.reference_name))) + frappe.throw(_("Exception occurred while reconciling {0}").format(payment.reference_name)) def submit_payment_entries(self): for payment in self.new_transaction_items: diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py index b8ebebaa93d..0e45db3dbc0 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -49,7 +49,7 @@ class BankTransaction(StatusUpdater): if paid_amount and allocated_amount: if flt(allocated_amount[0]["allocated_amount"]) > flt(paid_amount): - frappe.throw(_("The total allocated amount ({0}) is greated than the paid amount ({1}).".format(flt(allocated_amount[0]["allocated_amount"]), flt(paid_amount)))) + frappe.throw(_("The total allocated amount ({0}) is greated than the paid amount ({1}).").format(flt(allocated_amount[0]["allocated_amount"]), flt(paid_amount))) else: if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]: self.clear_simple_entry(payment_entry) diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py index deedafdfb5d..33ae45439e7 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction_upload.py @@ -15,8 +15,8 @@ def upload_bank_statement(): with open(frappe.uploaded_file, "rb") as upfile: fcontent = upfile.read() else: - from frappe.utils.file_manager import get_uploaded_content - fname, fcontent = get_uploaded_content() + fcontent = frappe.local.uploaded_file + fname = frappe.local.uploaded_filename if frappe.safe_encode(fname).lower().endswith("csv".encode('utf-8')): from frappe.utils.csvutils import read_csv_content diff --git a/erpnext/accounts/doctype/budget/budget.py b/erpnext/accounts/doctype/budget/budget.py index b76cdf3d1c4..084514cbfa7 100644 --- a/erpnext/accounts/doctype/budget/budget.py +++ b/erpnext/accounts/doctype/budget/budget.py @@ -210,10 +210,10 @@ def get_requested_amount(args, budget): item_code = args.get('item_code') condition = get_other_condition(args, budget, 'Material Request') - data = frappe.db.sql(""" select ifnull((sum(mri.stock_qty - mri.ordered_qty) * rate), 0) as amount - from `tabMaterial Request Item` mri, `tabMaterial Request` mr where mr.name = mri.parent and - mri.item_code = %s and mr.docstatus = 1 and mri.stock_qty > mri.ordered_qty and {0} and - mr.material_request_type = 'Purchase' and mr.status != 'Stopped'""".format(condition), item_code, as_list=1) + data = frappe.db.sql(""" select ifnull((sum(child.stock_qty - child.ordered_qty) * rate), 0) as amount + from `tabMaterial Request Item` child, `tabMaterial Request` parent where parent.name = child.parent and + child.item_code = %s and parent.docstatus = 1 and child.stock_qty > child.ordered_qty and {0} and + parent.material_request_type = 'Purchase' and parent.status != 'Stopped'""".format(condition), item_code, as_list=1) return data[0][0] if data else 0 @@ -221,10 +221,10 @@ def get_ordered_amount(args, budget): item_code = args.get('item_code') condition = get_other_condition(args, budget, 'Purchase Order') - data = frappe.db.sql(""" select ifnull(sum(poi.amount - poi.billed_amt), 0) as amount - from `tabPurchase Order Item` poi, `tabPurchase Order` po where - po.name = poi.parent and poi.item_code = %s and po.docstatus = 1 and poi.amount > poi.billed_amt - and po.status != 'Closed' and {0}""".format(condition), item_code, as_list=1) + data = frappe.db.sql(""" select ifnull(sum(child.amount - child.billed_amt), 0) as amount + from `tabPurchase Order Item` child, `tabPurchase Order` parent where + parent.name = child.parent and child.item_code = %s and parent.docstatus = 1 and child.amount > child.billed_amt + and parent.status != 'Closed' and {0}""".format(condition), item_code, as_list=1) return data[0][0] if data else 0 @@ -233,16 +233,15 @@ def get_other_condition(args, budget, for_doc): budget_against_field = frappe.scrub(args.get("budget_against_field")) if budget_against_field and args.get(budget_against_field): - condition += " and %s = '%s'" %(budget_against_field, args.get(budget_against_field)) + condition += " and child.%s = '%s'" %(budget_against_field, args.get(budget_against_field)) if args.get('fiscal_year'): date_field = 'schedule_date' if for_doc == 'Material Request' else 'transaction_date' start_date, end_date = frappe.db.get_value('Fiscal Year', args.get('fiscal_year'), ['year_start_date', 'year_end_date']) - alias = 'mr' if for_doc == 'Material Request' else 'po' - condition += """ and %s.%s - between '%s' and '%s' """ %(alias, date_field, start_date, end_date) + condition += """ and parent.%s + between '%s' and '%s' """ %(date_field, start_date, end_date) return condition diff --git a/erpnext/accounts/doctype/c_form/c_form.py b/erpnext/accounts/doctype/c_form/c_form.py index 2dcf9585563..9b64f8100fe 100644 --- a/erpnext/accounts/doctype/c_form/c_form.py +++ b/erpnext/accounts/doctype/c_form/c_form.py @@ -18,7 +18,7 @@ class CForm(Document): `tabSales Invoice` where name = %s and docstatus = 1""", d.invoice_no) if inv and inv[0][0] != 'Yes': - frappe.throw(_("C-form is not applicable for Invoice: {0}".format(d.invoice_no))) + frappe.throw(_("C-form is not applicable for Invoice: {0}").format(d.invoice_no)) elif inv and inv[0][1] and inv[0][1] != self.name: frappe.throw(_("""Invoice {0} is tagged in another C-form: {1}. diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py index 9bf5887b38f..362efef46c9 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py @@ -96,7 +96,11 @@ def build_forest(data): if parent_account == account_name == child: return [parent_account] elif account_name == child: - return [child] + return_parent(data, parent_account) + parent_account_list = return_parent(data, parent_account) + if not parent_account_list: + frappe.throw(_("The parent account {0} does not exists") + .format(parent_account)) + return [child] + parent_account_list charts_map, paths = {}, [] @@ -185,7 +189,8 @@ def validate_account_types(accounts): return _("Please identify/create Account (Ledger) for type - {0}").format(' , '.join(missing)) account_types_for_group = ["Bank", "Cash", "Stock"] - account_groups = [accounts[d]["account_type"] for d in accounts if accounts[d]['is_group'] not in ('', 1)] + # fix logic bug + account_groups = [accounts[d]["account_type"] for d in accounts if accounts[d]['is_group'] == 1] missing = list(set(account_types_for_group) - set(account_groups)) if missing: diff --git a/erpnext/accounts/doctype/cost_center/cost_center.json b/erpnext/accounts/doctype/cost_center/cost_center.json index 5149be216b3..976f05ad637 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.json +++ b/erpnext/accounts/doctype/cost_center/cost_center.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_copy": 1, "allow_import": 1, "allow_rename": 1, @@ -123,7 +124,8 @@ ], "icon": "fa fa-money", "idx": 1, - "modified": "2019-09-16 14:44:17.103548", + "links": [], + "modified": "2020-01-28 13:50:23.430434", "modified_by": "Administrator", "module": "Accounts", "name": "Cost Center", @@ -162,7 +164,6 @@ "role": "Purchase User" } ], - "quick_entry": 1, "search_fields": "parent_cost_center, is_group", "show_name_in_global_search": 1, "sort_field": "modified", diff --git a/erpnext/accounts/doctype/cost_center/cost_center.py b/erpnext/accounts/doctype/cost_center/cost_center.py index 584e11c53f6..0294e78111c 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center.py +++ b/erpnext/accounts/doctype/cost_center/cost_center.py @@ -18,6 +18,7 @@ class CostCenter(NestedSet): def validate(self): self.validate_mandatory() + self.validate_parent_cost_center() def validate_mandatory(self): if self.cost_center_name != self.company and not self.parent_cost_center: @@ -25,6 +26,12 @@ class CostCenter(NestedSet): elif self.cost_center_name == self.company and self.parent_cost_center: frappe.throw(_("Root cannot have a parent cost center")) + def validate_parent_cost_center(self): + if self.parent_cost_center: + if not frappe.db.get_value('Cost Center', self.parent_cost_center, 'is_group'): + frappe.throw(_("{0} is not a group node. Please select a group node as parent cost center").format( + frappe.bold(self.parent_cost_center))) + def convert_group_to_ledger(self): if self.check_if_child_exists(): frappe.throw(_("Cannot convert Cost Center to ledger as it has child nodes")) diff --git a/erpnext/accounts/doctype/cost_center/test_cost_center.py b/erpnext/accounts/doctype/cost_center/test_cost_center.py index c4fad753756..8f23d906760 100644 --- a/erpnext/accounts/doctype/cost_center/test_cost_center.py +++ b/erpnext/accounts/doctype/cost_center/test_cost_center.py @@ -1,12 +1,26 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals - - +import unittest import frappe + test_records = frappe.get_test_records('Cost Center') +class TestCostCenter(unittest.TestCase): + def test_cost_center_creation_against_child_node(self): + if not frappe.db.get_value('Cost Center', {'name': '_Test Cost Center 2 - _TC'}): + frappe.get_doc(test_records[1]).insert() + + cost_center = frappe.get_doc({ + 'doctype': 'Cost Center', + 'cost_center_name': '_Test Cost Center 3', + 'parent_cost_center': '_Test Cost Center 2 - _TC', + 'is_group': 0, + 'company': '_Test Company' + }) + + self.assertRaises(frappe.ValidationError, cost_center.save) def create_cost_center(**args): args = frappe._dict(args) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 078e05816db..041e419752b 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -29,7 +29,6 @@ class GLEntry(Document): self.validate_and_set_fiscal_year() self.pl_must_have_cost_center() self.validate_cost_center() - self.validate_dimensions_for_pl_and_bs() if not self.flags.from_repost: self.check_pl_account() @@ -39,6 +38,7 @@ class GLEntry(Document): def on_update_with_args(self, adv_adj, update_outstanding = 'Yes', from_repost=False): if not from_repost: self.validate_account_details(adv_adj) + self.validate_dimensions_for_pl_and_bs() check_freezing_date(self.posting_date, adv_adj) validate_frozen_account(self.account, adv_adj) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 221e3a72803..3604b60b751 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -190,7 +190,6 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({ if(jvd.reference_type==="Employee Advance") { return { filters: { - 'status': ['=', 'Unpaid'], 'docstatus': 1 } }; @@ -398,7 +397,7 @@ cur_frm.cscript.voucher_type = function(doc, cdt, cdn) { method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_default_bank_cash_account", args: { "account_type": (doc.voucher_type=="Bank Entry" ? - "Bank" : (doc.voucher_type=="Cash" ? "Cash" : null)), + "Bank" : (doc.voucher_type=="Cash Entry" ? "Cash" : null)), "company": doc.company }, callback: function(r) { diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json index 2f9f215fe86..f5991241a72 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.json +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json @@ -1,1780 +1,541 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2013-03-25 10:53:52", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 0, + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2013-03-25 10:53:52", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "entry_type_and_date", + "title", + "voucher_type", + "naming_series", + "column_break1", + "posting_date", + "company", + "finance_book", + "2_add_edit_gl_entries", + "accounts", + "section_break99", + "cheque_no", + "cheque_date", + "user_remark", + "column_break99", + "total_debit", + "total_credit", + "difference", + "get_balance", + "multi_currency", + "total_amount_currency", + "total_amount", + "total_amount_in_words", + "reference", + "clearance_date", + "remark", + "paid_loan", + "inter_company_journal_entry_reference", + "column_break98", + "bill_no", + "bill_date", + "due_date", + "write_off", + "write_off_based_on", + "get_outstanding_invoices", + "column_break_30", + "write_off_amount", + "printing_settings", + "pay_to_recd_from", + "column_break_35", + "letter_head", + "select_print_heading", + "addtional_info", + "mode_of_payment", + "payment_order", + "column_break3", + "is_opening", + "stock_entry", + "subscription_section", + "auto_repeat", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "entry_type_and_date", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "options": "fa fa-flag", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "entry_type_and_date", + "fieldtype": "Section Break", + "options": "fa fa-flag" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "title", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Title", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "fieldname": "title", + "fieldtype": "Data", + "hidden": 1, + "label": "Title", + "no_copy": 1, + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Journal Entry", - "fieldname": "voucher_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Entry Type", - "length": 0, - "no_copy": 0, - "oldfieldname": "voucher_type", - "oldfieldtype": "Select", - "options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Journal Entry", + "fieldname": "voucher_type", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Entry Type", + "oldfieldname": "voucher_type", + "oldfieldtype": "Select", + "options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Series", - "length": 0, - "no_copy": 1, - "oldfieldname": "naming_series", - "oldfieldtype": "Select", - "options": "ACC-JV-.YYYY.-", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "oldfieldname": "naming_series", + "oldfieldtype": "Select", + "options": "ACC-JV-.YYYY.-", + "print_hide": 1, + "reqd": 1, + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break1", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "column_break1", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "posting_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Posting Date", - "length": 0, - "no_copy": 1, - "oldfieldname": "posting_date", - "oldfieldtype": "Date", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting Date", + "no_copy": 1, + "oldfieldname": "posting_date", + "oldfieldtype": "Date", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Company", - "length": 0, - "no_copy": 0, - "oldfieldname": "company", - "oldfieldtype": "Link", - "options": "Company", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Company", + "oldfieldname": "company", + "oldfieldtype": "Link", + "options": "Company", + "print_hide": 1, + "remember_last_selected_value": 1, + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "finance_book", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Finance Book", - "length": 0, - "no_copy": 0, - "options": "Finance Book", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "finance_book", + "fieldtype": "Link", + "label": "Finance Book", + "options": "Finance Book" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "2_add_edit_gl_entries", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-table", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "2_add_edit_gl_entries", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break", + "options": "fa fa-table" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "accounts", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Accounting Entries", - "length": 0, - "no_copy": 0, - "oldfieldname": "entries", - "oldfieldtype": "Table", - "options": "Journal Entry Account", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "accounts", + "fieldtype": "Table", + "label": "Accounting Entries", + "oldfieldname": "entries", + "oldfieldtype": "Table", + "options": "Journal Entry Account", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break99", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break99", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "cheque_no", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Reference Number", - "length": 0, - "no_copy": 1, - "oldfieldname": "cheque_no", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "cheque_no", + "fieldtype": "Data", + "in_global_search": 1, + "in_list_view": 1, + "label": "Reference Number", + "no_copy": 1, + "oldfieldname": "cheque_no", + "oldfieldtype": "Data", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "cheque_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reference Date", - "length": 0, - "no_copy": 1, - "oldfieldname": "cheque_date", - "oldfieldtype": "Date", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "cheque_date", + "fieldtype": "Date", + "label": "Reference Date", + "no_copy": 1, + "oldfieldname": "cheque_date", + "oldfieldtype": "Date", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "user_remark", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "User Remark", - "length": 0, - "no_copy": 1, - "oldfieldname": "user_remark", - "oldfieldtype": "Small Text", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "user_remark", + "fieldtype": "Small Text", + "label": "User Remark", + "no_copy": 1, + "oldfieldname": "user_remark", + "oldfieldtype": "Small Text", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break99", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break99", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_debit", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Debit", - "length": 0, - "no_copy": 1, - "oldfieldname": "total_debit", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_debit", + "fieldtype": "Currency", + "label": "Total Debit", + "no_copy": 1, + "oldfieldname": "total_debit", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_credit", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Credit", - "length": 0, - "no_copy": 1, - "oldfieldname": "total_credit", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_credit", + "fieldtype": "Currency", + "label": "Total Credit", + "no_copy": 1, + "oldfieldname": "total_credit", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "difference", - "fieldname": "difference", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Difference (Dr - Cr)", - "length": 0, - "no_copy": 1, - "oldfieldname": "difference", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "difference", + "fieldname": "difference", + "fieldtype": "Currency", + "label": "Difference (Dr - Cr)", + "no_copy": 1, + "oldfieldname": "difference", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "difference", - "fieldname": "get_balance", - "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Make Difference Entry", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Button", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "difference", + "fieldname": "get_balance", + "fieldtype": "Button", + "label": "Make Difference Entry", + "oldfieldtype": "Button" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "multi_currency", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Multi Currency", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "multi_currency", + "fieldtype": "Check", + "label": "Multi Currency", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_amount_currency", - "fieldtype": "Link", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Amount Currency", - "length": 0, - "no_copy": 1, - "options": "Currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_amount_currency", + "fieldtype": "Link", + "hidden": 1, + "label": "Total Amount Currency", + "no_copy": 1, + "options": "Currency", + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "fieldname": "total_amount", - "fieldtype": "Currency", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Total Amount", - "length": 0, - "no_copy": 1, - "options": "total_amount_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "bold": 1, + "fieldname": "total_amount", + "fieldtype": "Currency", + "hidden": 1, + "in_list_view": 1, + "label": "Total Amount", + "no_copy": 1, + "options": "total_amount_currency", + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_amount_in_words", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Amount in Words", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_amount_in_words", + "fieldtype": "Data", + "hidden": 1, + "label": "Total Amount in Words", + "no_copy": 1, + "print_hide": 1, + "read_only": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "reference", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reference", - "length": 0, - "no_copy": 0, - "options": "fa fa-pushpin", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "reference", + "fieldtype": "Section Break", + "label": "Reference", + "options": "fa fa-pushpin" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "clearance_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Clearance Date", - "length": 0, - "no_copy": 1, - "oldfieldname": "clearance_date", - "oldfieldtype": "Date", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "clearance_date", + "fieldtype": "Date", + "label": "Clearance Date", + "no_copy": 1, + "oldfieldname": "clearance_date", + "oldfieldtype": "Date", + "read_only": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "remark", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Remark", - "length": 0, - "no_copy": 1, - "oldfieldname": "remark", - "oldfieldtype": "Small Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "remark", + "fieldtype": "Small Text", + "in_global_search": 1, + "label": "Remark", + "no_copy": 1, + "oldfieldname": "remark", + "oldfieldtype": "Small Text", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "paid_loan", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Paid Loan", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "paid_loan", + "fieldtype": "Data", + "hidden": 1, + "label": "Paid Loan", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.voucher_type== \"Inter Company Journal Entry\"", - "fieldname": "inter_company_journal_entry_reference", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Inter Company Journal Entry Reference", - "length": 0, - "no_copy": 0, - "options": "Journal Entry", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.voucher_type== \"Inter Company Journal Entry\"", + "fieldname": "inter_company_journal_entry_reference", + "fieldtype": "Link", + "label": "Inter Company Journal Entry Reference", + "options": "Journal Entry", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break98", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break98", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bill_no", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bill No", - "length": 0, - "no_copy": 0, - "oldfieldname": "bill_no", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "bill_no", + "fieldtype": "Data", + "label": "Bill No", + "oldfieldname": "bill_no", + "oldfieldtype": "Data", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bill_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bill Date", - "length": 0, - "no_copy": 0, - "oldfieldname": "bill_date", - "oldfieldtype": "Date", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "bill_date", + "fieldtype": "Date", + "label": "Bill Date", + "oldfieldname": "bill_date", + "oldfieldtype": "Date", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "due_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Due Date", - "length": 0, - "no_copy": 0, - "oldfieldname": "due_date", - "oldfieldtype": "Date", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "due_date", + "fieldtype": "Date", + "label": "Due Date", + "oldfieldname": "due_date", + "oldfieldtype": "Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "depends_on": "eval:doc.voucher_type == 'Write Off Entry'", - "fieldname": "write_off", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Write Off", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "depends_on": "eval:doc.voucher_type == 'Write Off Entry'", + "fieldname": "write_off", + "fieldtype": "Section Break", + "label": "Write Off" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Accounts Receivable", - "depends_on": "eval:doc.voucher_type == 'Write Off Entry'", - "fieldname": "write_off_based_on", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Write Off Based On", - "length": 0, - "no_copy": 0, - "options": "Accounts Receivable\nAccounts Payable", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Accounts Receivable", + "depends_on": "eval:doc.voucher_type == 'Write Off Entry'", + "fieldname": "write_off_based_on", + "fieldtype": "Select", + "label": "Write Off Based On", + "options": "Accounts Receivable\nAccounts Payable", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.voucher_type == 'Write Off Entry'", - "fieldname": "get_outstanding_invoices", - "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Get Outstanding Invoices", - "length": 0, - "no_copy": 0, - "options": "get_outstanding_invoices", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.voucher_type == 'Write Off Entry'", + "fieldname": "get_outstanding_invoices", + "fieldtype": "Button", + "label": "Get Outstanding Invoices", + "options": "get_outstanding_invoices", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_30", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_30", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.voucher_type == 'Write Off Entry'", - "fieldname": "write_off_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Write Off Amount", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.voucher_type == 'Write Off Entry'", + "fieldname": "write_off_amount", + "fieldtype": "Currency", + "label": "Write Off Amount", + "options": "Company:company:default_currency", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "printing_settings", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Printing Settings", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "printing_settings", + "fieldtype": "Section Break", + "label": "Printing Settings" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "pay_to_recd_from", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Pay To / Recd From", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "fieldname": "pay_to_recd_from", + "fieldtype": "Data", + "label": "Pay To / Recd From", + "no_copy": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_35", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_35", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "letter_head", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Letter Head", - "length": 0, - "no_copy": 0, - "options": "Letter Head", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "fieldname": "letter_head", + "fieldtype": "Link", + "label": "Letter Head", + "options": "Letter Head" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "select_print_heading", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Print Heading", - "length": 0, - "no_copy": 1, - "oldfieldname": "select_print_heading", - "oldfieldtype": "Link", - "options": "Print Heading", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "fieldname": "select_print_heading", + "fieldtype": "Link", + "label": "Print Heading", + "no_copy": 1, + "oldfieldname": "select_print_heading", + "oldfieldtype": "Link", + "options": "Print Heading", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "addtional_info", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "More Information", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-file-text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "addtional_info", + "fieldtype": "Section Break", + "label": "More Information", + "oldfieldtype": "Section Break", + "options": "fa fa-file-text" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mode_of_payment", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Mode of Payment", - "length": 0, - "no_copy": 0, - "options": "Mode of Payment", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "mode_of_payment", + "fieldtype": "Link", + "label": "Mode of Payment", + "options": "Mode of Payment" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "payment_order", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payment Order", - "length": 0, - "no_copy": 1, - "options": "Payment Order", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "payment_order", + "fieldtype": "Link", + "label": "Payment Order", + "no_copy": 1, + "options": "Payment Order", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "column_break3", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "No", - "description": "", - "fieldname": "is_opening", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Opening", - "length": 0, - "no_copy": 0, - "oldfieldname": "is_opening", - "oldfieldtype": "Select", - "options": "No\nYes", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "No", + "fieldname": "is_opening", + "fieldtype": "Select", + "label": "Is Opening", + "oldfieldname": "is_opening", + "oldfieldtype": "Select", + "options": "No\nYes", + "print_hide": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:in_list([\"Credit Note\", \"Debit Note\"], doc.voucher_type)", - "fieldname": "stock_entry", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Stock Entry", - "length": 0, - "no_copy": 0, - "options": "Stock Entry", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:in_list([\"Credit Note\", \"Debit Note\"], doc.voucher_type)", + "fieldname": "stock_entry", + "fieldtype": "Link", + "label": "Stock Entry", + "options": "Stock Entry", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "subscription_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Subscription Section", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "subscription_section", + "fieldtype": "Section Break", + "label": "Subscription Section" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "auto_repeat", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Auto Repeat", - "length": 0, - "no_copy": 1, - "options": "Auto Repeat", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "fieldname": "auto_repeat", + "fieldtype": "Link", + "label": "Auto Repeat", + "no_copy": 1, + "options": "Auto Repeat", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "oldfieldname": "amended_from", - "oldfieldtype": "Link", - "options": "Journal Entry", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "amended_from", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Amended From", + "no_copy": 1, + "oldfieldname": "amended_from", + "oldfieldtype": "Link", + "options": "Journal Entry", + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-file-text", - "idx": 176, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "menu_index": 0, - "modified": "2019-01-07 16:52:02.838365", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Journal Entry", - "owner": "Administrator", + ], + "icon": "fa fa-file-text", + "idx": 176, + "is_submittable": 1, + "modified": "2020-01-16 13:05:30.634226", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Journal Entry", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Auditor", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Auditor" } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 1, - "search_fields": "voucher_type,posting_date, due_date, cheque_no", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "title", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "search_fields": "voucher_type,posting_date, due_date, cheque_no", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "title", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index e25942ca34d..458e4a25260 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -616,7 +616,7 @@ class JournalEntry(AccountsController): d.reference_name, ("total_sanctioned_amount", "total_amount_reimbursed")) pending_amount = flt(sanctioned_amount) - flt(reimbursed_amount) if d.debit > pending_amount: - frappe.throw(_("Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2}".format(d.idx, d.reference_name, pending_amount))) + frappe.throw(_("Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2}").format(d.idx, d.reference_name, pending_amount)) def validate_credit_debit_note(self): if self.stock_entry: @@ -624,7 +624,7 @@ class JournalEntry(AccountsController): frappe.throw(_("Stock Entry {0} is not submitted").format(self.stock_entry)) if frappe.db.exists({"doctype": "Journal Entry", "stock_entry": self.stock_entry, "docstatus":1}): - frappe.msgprint(_("Warning: Another {0} # {1} exists against stock entry {2}".format(self.voucher_type, self.name, self.stock_entry))) + frappe.msgprint(_("Warning: Another {0} # {1} exists against stock entry {2}").format(self.voucher_type, self.name, self.stock_entry)) def validate_empty_accounts_table(self): if not self.get('accounts'): @@ -968,7 +968,7 @@ def get_exchange_rate(posting_date, account=None, account_currency=None, company # The date used to retreive the exchange rate here is the date passed # in as an argument to this function. - elif (not exchange_rate or exchange_rate==1) and account_currency and posting_date: + elif (not exchange_rate or flt(exchange_rate)==1) and account_currency and posting_date: exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date) else: exchange_rate = 1 diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json index ab811d81b25..9552e60a857 100644 --- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json @@ -90,7 +90,6 @@ "fieldtype": "Column Break" }, { - "default": "Customer", "fieldname": "party_type", "fieldtype": "Link", "in_list_view": 1, @@ -201,7 +200,7 @@ "fieldname": "reference_type", "fieldtype": "Select", "label": "Reference Type", - "options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting" + "options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees" }, { "fieldname": "reference_name", @@ -272,7 +271,7 @@ ], "idx": 1, "istable": 1, - "modified": "2019-10-02 12:23:21.693443", + "modified": "2020-01-13 12:41:33.968025", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry Account", @@ -281,4 +280,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 1e0b1bcbf16..2192b7bf989 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -554,7 +554,7 @@ frappe.ui.form.on('Payment Entry', { frappe.flags.allocate_payment_amount = true; frm.events.validate_filters_data(frm, filters); frm.events.get_outstanding_documents(frm, filters); - }, __("Filters"), __("Get Outstanding Invoices")); + }, __("Filters"), __("Get Outstanding Documents")); }, validate_filters_data: function(frm, filters) { @@ -652,14 +652,16 @@ frappe.ui.form.on('Payment Entry', { (frm.doc.payment_type=="Receive" && frm.doc.party_type=="Student") ) { if(total_positive_outstanding > total_negative_outstanding) - frm.set_value("paid_amount", - total_positive_outstanding - total_negative_outstanding); + if (!frm.doc.paid_amount) + frm.set_value("paid_amount", + total_positive_outstanding - total_negative_outstanding); } else if ( total_negative_outstanding && total_positive_outstanding < total_negative_outstanding ) { - frm.set_value("received_amount", - total_negative_outstanding - total_positive_outstanding); + if (!frm.doc.received_amount) + frm.set_value("received_amount", + total_negative_outstanding - total_positive_outstanding); } } diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index acfc660c4f7..997937738b6 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -332,6 +332,7 @@ "label": "Reference" }, { + "depends_on": "eval:doc.docstatus==0", "fieldname": "get_outstanding_invoice", "fieldtype": "Button", "label": "Get Outstanding Invoice" @@ -575,7 +576,7 @@ } ], "is_submittable": 1, - "modified": "2019-11-06 12:59:43.151721", + "modified": "2019-12-08 13:02:30.016610", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index bf7e833285c..55d275831ec 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -102,7 +102,9 @@ class PaymentEntry(AccountsController): self.bank = bank_data.bank self.bank_account_no = bank_data.bank_account_no - self.set(field, bank_data.account) + + if not self.get(field): + self.set(field, bank_data.account) def validate_allocated_amount(self): for d in self.get("references"): @@ -911,7 +913,10 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= else: party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company) - party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account) + if dt not in ("Sales Invoice", "Purchase Invoice"): + party_account_currency = get_account_currency(party_account) + else: + party_account_currency = doc.get("party_account_currency") or get_account_currency(party_account) # payment type if (dt == "Sales Order" or (dt in ("Sales Invoice", "Fees") and doc.outstanding_amount > 0)) \ @@ -931,9 +936,9 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= grand_total = doc.rounded_total or doc.grand_total outstanding_amount = doc.outstanding_amount elif dt in ("Expense Claim"): - grand_total = doc.total_sanctioned_amount - outstanding_amount = doc.total_sanctioned_amount \ - - doc.total_amount_reimbursed - flt(doc.total_advance_amount) + grand_total = doc.total_sanctioned_amount + doc.total_taxes_and_charges + outstanding_amount = doc.grand_total \ + - doc.total_amount_reimbursed elif dt == "Employee Advance": grand_total = doc.advance_amount outstanding_amount = flt(doc.advance_amount) - flt(doc.paid_amount) @@ -1000,7 +1005,7 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= # only Purchase Invoice can be blocked individually if doc.doctype == "Purchase Invoice" and doc.invoice_is_blocked(): - frappe.msgprint(_('{0} is on hold till {1}'.format(doc.name, doc.release_date))) + frappe.msgprint(_('{0} is on hold till {1}').format(doc.name, doc.release_date)) else: pe.append("references", { 'reference_doctype': dt, diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index d85344e8b7a..2c04a27b0cd 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -23,6 +23,8 @@ class PaymentReconciliation(Document): if self.party_type in ["Customer", "Supplier"]: dr_or_cr_notes = self.get_dr_or_cr_notes() + else: + dr_or_cr_notes = [] self.add_payment_entries(payment_entries + journal_entries + dr_or_cr_notes) diff --git a/erpnext/accounts/doctype/payment_request/payment_request.js b/erpnext/accounts/doctype/payment_request/payment_request.js index e2510f675f3..e1e43140c01 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.js +++ b/erpnext/accounts/doctype/payment_request/payment_request.js @@ -2,6 +2,16 @@ cur_frm.add_fetch("payment_gateway_account", "payment_account", "payment_account cur_frm.add_fetch("payment_gateway_account", "payment_gateway", "payment_gateway") cur_frm.add_fetch("payment_gateway_account", "message", "message") +frappe.ui.form.on("Payment Request", { + setup: function(frm) { + frm.set_query("party_type", function() { + return { + query: "erpnext.setup.doctype.party_type.party_type.get_party_type", + }; + }); + } +}) + frappe.ui.form.on("Payment Request", "onload", function(frm, dt, dn){ if (frm.doc.reference_doctype) { frappe.call({ diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index eda59abf04e..0fade8c456c 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -39,8 +39,8 @@ class PaymentRequest(Document): ref_amount = get_amount(ref_doc) if existing_payment_request_amount + flt(self.grand_total)> ref_amount: - frappe.throw(_("Total Payment Request amount cannot be greater than {0} amount" - .format(self.reference_doctype))) + frappe.throw(_("Total Payment Request amount cannot be greater than {0} amount") + .format(self.reference_doctype)) def validate_currency(self): ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) @@ -53,14 +53,14 @@ class PaymentRequest(Document): for subscription_plan in self.subscription_plans: payment_gateway = frappe.db.get_value("Subscription Plan", subscription_plan.plan, "payment_gateway") if payment_gateway != self.payment_gateway_account: - frappe.throw(_('The payment gateway account in plan {0} is different from the payment gateway account in this payment request'.format(subscription_plan.name))) + frappe.throw(_('The payment gateway account in plan {0} is different from the payment gateway account in this payment request').format(subscription_plan.name)) rate = get_plan_rate(subscription_plan.plan, quantity=subscription_plan.qty) amount += rate if amount != self.grand_total: - frappe.msgprint(_("The amount of {0} set in this payment request is different from the calculated amount of all payment plans: {1}. Make sure this is correct before submitting the document.".format(self.grand_total, amount))) + frappe.msgprint(_("The amount of {0} set in this payment request is different from the calculated amount of all payment plans: {1}. Make sure this is correct before submitting the document.").format(self.grand_total, amount)) def on_submit(self): if self.payment_request_type == 'Outward': @@ -350,13 +350,13 @@ def get_amount(ref_doc): if dt in ["Sales Order", "Purchase Order"]: grand_total = flt(ref_doc.grand_total) - flt(ref_doc.advance_paid) - if dt in ["Sales Invoice", "Purchase Invoice"]: + elif dt in ["Sales Invoice", "Purchase Invoice"]: if ref_doc.party_account_currency == ref_doc.currency: grand_total = flt(ref_doc.outstanding_amount) else: grand_total = flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate - if dt == "Fees": + elif dt == "Fees": grand_total = ref_doc.outstanding_amount if grand_total > 0 : @@ -373,6 +373,7 @@ def get_existing_payment_request_amount(ref_dt, ref_dn): reference_doctype = %s and reference_name = %s and docstatus = 1 + and status != 'Paid' """, (ref_dt, ref_dn)) return flt(existing_payment_request_amount[0][0]) if existing_payment_request_amount else 0 diff --git a/erpnext/assets/doctype/asset_settings/__init__.py b/erpnext/accounts/doctype/pos_field/__init__.py similarity index 100% rename from erpnext/assets/doctype/asset_settings/__init__.py rename to erpnext/accounts/doctype/pos_field/__init__.py diff --git a/erpnext/accounts/doctype/pos_field/pos_field.json b/erpnext/accounts/doctype/pos_field/pos_field.json new file mode 100644 index 00000000000..13edabd985d --- /dev/null +++ b/erpnext/accounts/doctype/pos_field/pos_field.json @@ -0,0 +1,77 @@ +{ + "creation": "2019-08-22 14:35:39.043242", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "fieldname", + "label", + "fieldtype", + "column_break_7", + "options", + "default_value", + "reqd", + "read_only" + ], + "fields": [ + { + "fieldname": "fieldname", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Fieldname" + }, + { + "fieldname": "fieldtype", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Fieldtype", + "read_only": 1 + }, + { + "fieldname": "label", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Label", + "read_only": 1 + }, + { + "fieldname": "options", + "fieldtype": "Text", + "in_list_view": 1, + "label": "Options", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "reqd", + "fieldtype": "Check", + "label": "Mandatory" + }, + { + "default": "0", + "fieldname": "read_only", + "fieldtype": "Check", + "label": "Read Only" + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "fieldname": "default_value", + "fieldtype": "Data", + "label": "Default Value" + } + ], + "istable": 1, + "modified": "2019-08-23 13:59:34.025523", + "modified_by": "Administrator", + "module": "Accounts", + "name": "POS Field", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/setup/doctype/setup_progress_action/setup_progress_action.py b/erpnext/accounts/doctype/pos_field/pos_field.py similarity index 61% rename from erpnext/setup/doctype/setup_progress_action/setup_progress_action.py rename to erpnext/accounts/doctype/pos_field/pos_field.py index 24af94347e5..b4720b309bd 100644 --- a/erpnext/setup/doctype/setup_progress_action/setup_progress_action.py +++ b/erpnext/accounts/doctype/pos_field/pos_field.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt from __future__ import unicode_literals +# import frappe from frappe.model.document import Document -class SetupProgressAction(Document): +class POSField(Document): pass diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json index aa9f85a05ab..fba1bed9dd1 100644 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.json +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json @@ -3,6 +3,7 @@ "autoname": "Prompt", "creation": "2013-05-24 12:15:51", "doctype": "DocType", + "engine": "InnoDB", "field_order": [ "disabled", "section_break_2", @@ -50,6 +51,7 @@ "income_account", "expense_account", "taxes_and_charges", + "tax_category", "apply_discount_on", "accounting_dimensions_section", "cost_center", @@ -381,11 +383,17 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" + }, + { + "fieldname": "tax_category", + "fieldtype": "Link", + "label": "Tax Category", + "options": "Tax Category" } ], "icon": "icon-cog", "idx": 1, - "modified": "2019-05-25 22:56:30.352693", + "modified": "2020-01-24 15:52:03.797701", "modified_by": "Administrator", "module": "Accounts", "name": "POS Profile", diff --git a/erpnext/accounts/doctype/pos_settings/pos_settings.js b/erpnext/accounts/doctype/pos_settings/pos_settings.js index 1a146185139..f5b681bd41d 100644 --- a/erpnext/accounts/doctype/pos_settings/pos_settings.js +++ b/erpnext/accounts/doctype/pos_settings/pos_settings.js @@ -2,7 +2,46 @@ // For license information, please see license.txt frappe.ui.form.on('POS Settings', { - refresh: function() { + onload: function(frm) { + frm.trigger("get_invoice_fields"); + }, + use_pos_in_offline_mode: function(frm) { + frm.trigger("get_invoice_fields"); + }, + + get_invoice_fields: function(frm) { + if (!frm.doc.use_pos_in_offline_mode) { + frappe.model.with_doctype("Sales Invoice", () => { + var fields = $.map(frappe.get_doc("DocType", "Sales Invoice").fields, function(d) { + if (frappe.model.no_value_type.indexOf(d.fieldtype) === -1 || + d.fieldtype === 'Table') { + return { label: d.label + ' (' + d.fieldtype + ')', value: d.fieldname }; + } else { + return null; + } + }); + + frappe.meta.get_docfield("POS Field", "fieldname", frm.doc.name).options = [""].concat(fields); + }); + } else { + frappe.meta.get_docfield("POS Field", "fieldname", frm.doc.name).options = [""]; + } + } +}); + +frappe.ui.form.on("POS Field", { + fieldname: function(frm, doctype, name) { + var doc = frappe.get_doc(doctype, name); + var df = $.map(frappe.get_doc("DocType", "Sales Invoice").fields, function(d) { + return doc.fieldname == d.fieldname ? d : null; + })[0]; + + doc.label = df.label; + doc.reqd = df.reqd; + doc.options = df.options; + doc.fieldtype = df.fieldtype; + doc.default_value = df.default; + frm.refresh_field("fields"); } }); diff --git a/erpnext/accounts/doctype/pos_settings/pos_settings.json b/erpnext/accounts/doctype/pos_settings/pos_settings.json index 8f5b631c89a..1d55880415f 100644 --- a/erpnext/accounts/doctype/pos_settings/pos_settings.json +++ b/erpnext/accounts/doctype/pos_settings/pos_settings.json @@ -1,133 +1,68 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-08-28 16:46:41.732676", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2017-08-28 16:46:41.732676", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "use_pos_in_offline_mode", + "section_break_2", + "fields" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "use_pos_in_offline_mode", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Use POS in Offline Mode", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "default": "0", + "fieldname": "use_pos_in_offline_mode", + "fieldtype": "Check", + "label": "Use POS in Offline Mode" + }, + { + "fieldname": "section_break_2", + "fieldtype": "Section Break" + }, + { + "depends_on": "eval:!doc.use_pos_in_offline_mode", + "fieldname": "fields", + "fieldtype": "Table", + "label": "POS Field", + "options": "POS Field" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "modified": "2017-09-11 13:57:28.787023", - "modified_by": "Administrator", - "module": "Accounts", - "name": "POS Settings", - "name_case": "", - "owner": "Administrator", + ], + "issingle": 1, + "links": [], + "modified": "2019-12-26 11:50:47.122997", + "modified_by": "Administrator", + "module": "Accounts", + "name": "POS Settings", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "email": 1, + "print": 1, + "read": 1, + "role": "Accounts User", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "Sales User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "email": 1, + "print": 1, + "read": 1, + "role": "Sales User", + "share": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json index 971d308368a..29d83783d07 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json @@ -389,8 +389,7 @@ "fieldname": "rate_or_discount", "fieldtype": "Select", "label": "Rate or Discount", - "options": "\nRate\nDiscount Percentage\nDiscount Amount", - "reqd": 1 + "options": "\nRate\nDiscount Percentage\nDiscount Amount" }, { "default": "Grand Total", @@ -439,19 +438,20 @@ }, { "default": "0", - "depends_on": "eval:!doc.mixed_conditions", + "depends_on": "eval:!doc.mixed_conditions && doc.apply_on != 'Transaction'", "fieldname": "same_item", "fieldtype": "Check", "label": "Same Item" }, { - "depends_on": "eval:!doc.same_item || doc.mixed_conditions", + "depends_on": "eval:(!doc.same_item || doc.apply_on == 'Transaction') || doc.mixed_conditions", "fieldname": "free_item", "fieldtype": "Link", "label": "Free Item", "options": "Item" }, { + "default": "0", "fieldname": "free_qty", "fieldtype": "Float", "label": "Qty" @@ -554,7 +554,7 @@ ], "icon": "fa fa-gift", "idx": 1, - "modified": "2019-10-15 12:39:40.399792", + "modified": "2019-12-18 17:29:22.957077", "modified_by": "Administrator", "module": "Accounts", "name": "Pricing Rule", diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 17762755f42..924e1081307 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -34,8 +34,7 @@ class PricingRule(Document): def validate_duplicate_apply_on(self): field = apply_on_dict.get(self.apply_on) - values = [d.get(frappe.scrub(self.apply_on)) for d in self.get(field)] - + values = [d.get(frappe.scrub(self.apply_on)) for d in self.get(field) if field] if len(values) != len(set(values)): frappe.throw(_("Duplicate {0} found in the table").format(self.apply_on)) @@ -48,18 +47,21 @@ class PricingRule(Document): if tocheck and not self.get(tocheck): throw(_("{0} is required").format(self.meta.get_label(tocheck)), frappe.MandatoryError) + if self.price_or_product_discount == 'Price' and not self.rate_or_discount: + throw(_("Rate or Discount is required for the price discount."), frappe.MandatoryError) + def validate_applicable_for_selling_or_buying(self): if not self.selling and not self.buying: throw(_("Atleast one of the Selling or Buying must be selected")) if not self.selling and self.applicable_for in ["Customer", "Customer Group", "Territory", "Sales Partner", "Campaign"]: - throw(_("Selling must be checked, if Applicable For is selected as {0}" - .format(self.applicable_for))) + throw(_("Selling must be checked, if Applicable For is selected as {0}") + .format(self.applicable_for)) if not self.buying and self.applicable_for in ["Supplier", "Supplier Group"]: - throw(_("Buying must be checked, if Applicable For is selected as {0}" - .format(self.applicable_for))) + throw(_("Buying must be checked, if Applicable For is selected as {0}") + .format(self.applicable_for)) def validate_min_max_qty(self): if self.min_qty and self.max_qty and flt(self.min_qty) > flt(self.max_qty): @@ -181,8 +183,9 @@ def get_serial_no_for_item(args): item_details.serial_no = get_serial_no(args) return item_details -def get_pricing_rule_for_item(args, price_list_rate=0, doc=None): - from erpnext.accounts.doctype.pricing_rule.utils import get_pricing_rules +def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=False): + from erpnext.accounts.doctype.pricing_rule.utils import (get_pricing_rules, + get_applied_pricing_rules, get_pricing_rule_items, get_product_discount_rule) if isinstance(doc, string_types): doc = json.loads(doc) @@ -209,6 +212,57 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None): item_details, args.get('item_code')) return item_details + update_args_for_pricing_rule(args) + + pricing_rules = (get_applied_pricing_rules(args) + if for_validate and args.get("pricing_rules") else get_pricing_rules(args, doc)) + + if pricing_rules: + rules = [] + + for pricing_rule in pricing_rules: + if not pricing_rule: continue + + if isinstance(pricing_rule, string_types): + pricing_rule = frappe.get_cached_doc("Pricing Rule", pricing_rule) + pricing_rule.apply_rule_on_other_items = get_pricing_rule_items(pricing_rule) + + if pricing_rule.get('suggestion'): continue + + item_details.validate_applied_rule = pricing_rule.get("validate_applied_rule", 0) + item_details.price_or_product_discount = pricing_rule.get("price_or_product_discount") + + rules.append(get_pricing_rule_details(args, pricing_rule)) + + if pricing_rule.mixed_conditions or pricing_rule.apply_rule_on_other: + item_details.update({ + 'apply_rule_on_other_items': json.dumps(pricing_rule.apply_rule_on_other_items), + 'apply_rule_on': (frappe.scrub(pricing_rule.apply_rule_on_other) + if pricing_rule.apply_rule_on_other else frappe.scrub(pricing_rule.get('apply_on'))) + }) + + if pricing_rule.coupon_code_based==1 and args.coupon_code==None: + return item_details + + if not pricing_rule.validate_applied_rule: + if pricing_rule.price_or_product_discount == "Price": + apply_price_discount_rule(pricing_rule, item_details, args) + else: + get_product_discount_rule(pricing_rule, item_details, doc) + + item_details.has_pricing_rule = 1 + + item_details.pricing_rules = ','.join([d.pricing_rule for d in rules]) + + if not doc: return item_details + + elif args.get("pricing_rules"): + item_details = remove_pricing_rule_for_item(args.get("pricing_rules"), + item_details, args.get('item_code')) + + return item_details + +def update_args_for_pricing_rule(args): if not (args.item_group and args.brand): try: args.item_group, args.brand = frappe.get_cached_value("Item", args.item_code, ["item_group", "brand"]) @@ -235,56 +289,16 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None): args.supplier_group = frappe.get_cached_value("Supplier", args.supplier, "supplier_group") args.customer = args.customer_group = args.territory = None - pricing_rules = get_pricing_rules(args, doc) - - if pricing_rules: - rules = [] - - for pricing_rule in pricing_rules: - if not pricing_rule or pricing_rule.get('suggestion'): continue - - item_details.validate_applied_rule = pricing_rule.get("validate_applied_rule", 0) - - rules.append(get_pricing_rule_details(args, pricing_rule)) - if pricing_rule.mixed_conditions or pricing_rule.apply_rule_on_other: - continue - - if pricing_rule.coupon_code_based==1 and args.coupon_code==None: - return item_details - - if (not pricing_rule.validate_applied_rule and - pricing_rule.price_or_product_discount == "Price"): - apply_price_discount_pricing_rule(pricing_rule, item_details, args) - - item_details.has_pricing_rule = 1 - - # if discount is applied on the rate and not on price list rate - # if price_list_rate: - # set_discount_amount(price_list_rate, item_details) - - item_details.pricing_rules = ','.join([d.pricing_rule for d in rules]) - - if not doc: return item_details - - for rule in rules: - doc.append('pricing_rules', rule) - - elif args.get("pricing_rules"): - item_details = remove_pricing_rule_for_item(args.get("pricing_rules"), - item_details, args.get('item_code')) - - return item_details - def get_pricing_rule_details(args, pricing_rule): return frappe._dict({ 'pricing_rule': pricing_rule.name, 'rate_or_discount': pricing_rule.rate_or_discount, 'margin_type': pricing_rule.margin_type, - 'item_code': pricing_rule.item_code or args.get("item_code"), + 'item_code': args.get("item_code"), 'child_docname': args.get('child_docname') }) -def apply_price_discount_pricing_rule(pricing_rule, item_details, args): +def apply_price_discount_rule(pricing_rule, item_details, args): item_details.pricing_rule_for = pricing_rule.rate_or_discount if ((pricing_rule.margin_type == 'Amount' and pricing_rule.currency == args.currency) @@ -327,10 +341,10 @@ def set_discount_amount(rate, item_details): item_details.rate = rate def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None): - from erpnext.accounts.doctype.pricing_rule.utils import get_apply_on_and_items + from erpnext.accounts.doctype.pricing_rule.utils import get_pricing_rule_items for d in pricing_rules.split(','): if not d or not frappe.db.exists("Pricing Rule", d): continue - pricing_rule = frappe.get_doc('Pricing Rule', d) + pricing_rule = frappe.get_cached_doc('Pricing Rule', d) if pricing_rule.price_or_product_discount == 'Price': if pricing_rule.rate_or_discount == 'Discount Percentage': @@ -348,8 +362,9 @@ def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None): else pricing_rule.get('free_item')) if pricing_rule.get("mixed_conditions") or pricing_rule.get("apply_rule_on_other"): - apply_on, items = get_apply_on_and_items(pricing_rule, item_details) - item_details.apply_on = apply_on + items = get_pricing_rule_items(pricing_rule) + item_details.apply_on = (frappe.scrub(pricing_rule.apply_rule_on_other) + if pricing_rule.apply_rule_on_other else frappe.scrub(pricing_rule.get('apply_on'))) item_details.applied_on_items = ','.join(items) item_details.pricing_rules = '' diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 332a9007915..9c1fef69fac 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -9,6 +9,8 @@ from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_orde from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.stock.get_item_details import get_item_details from frappe import MandatoryError +from erpnext.stock.doctype.item.test_item import make_item +from erpnext.healthcare.doctype.lab_test_template.lab_test_template import make_item_price class TestPricingRule(unittest.TestCase): def setUp(self): @@ -145,6 +147,52 @@ class TestPricingRule(unittest.TestCase): self.assertEquals(details.get("margin_type"), "Percentage") self.assertEquals(details.get("margin_rate_or_amount"), 10) + def test_mixed_conditions_for_item_group(self): + for item in ["Mixed Cond Item 1", "Mixed Cond Item 2"]: + make_item(item, {"item_group": "Products"}) + make_item_price(item, "_Test Price List", 100) + + test_record = { + "doctype": "Pricing Rule", + "title": "_Test Pricing Rule for Item Group", + "apply_on": "Item Group", + "item_groups": [ + { + "item_group": "Products", + }, + { + "item_group": "Seed", + }, + ], + "selling": 1, + "mixed_conditions": 1, + "currency": "USD", + "rate_or_discount": "Discount Percentage", + "discount_percentage": 10, + "applicable_for": "Customer Group", + "customer_group": "All Customer Groups", + "company": "_Test Company" + } + frappe.get_doc(test_record.copy()).insert() + + args = frappe._dict({ + "item_code": "Mixed Cond Item 1", + "item_group": "Products", + "company": "_Test Company", + "price_list": "_Test Price List", + "currency": "_Test Currency", + "doctype": "Sales Order", + "conversion_rate": 1, + "price_list_currency": "_Test Currency", + "plc_conversion_rate": 1, + "order_type": "Sales", + "customer": "_Test Customer", + "customer_group": "_Test Customer Group", + "name": None + }) + details = get_item_details(args) + self.assertEquals(details.get("discount_percentage"), 10) + def test_pricing_rule_for_variants(self): from erpnext.stock.get_item_details import get_item_details from frappe import MandatoryError diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index ef26c2e7bfd..e475563c77c 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -7,7 +7,8 @@ from __future__ import unicode_literals import frappe, copy, json from frappe import throw, _ from six import string_types -from frappe.utils import flt, cint, get_datetime +from frappe.utils import flt, cint, get_datetime, get_link_to_form, today +from erpnext.setup.doctype.item_group.item_group import get_child_item_groups from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses from erpnext.stock.get_item_details import get_conversion_factor @@ -173,10 +174,11 @@ def filter_pricing_rules(args, pricing_rules, doc=None): if (field and pricing_rules[0].get('other_' + field) != args.get(field)): return - pr_doc = frappe.get_doc('Pricing Rule', pricing_rules[0].name) + pr_doc = frappe.get_cached_doc('Pricing Rule', pricing_rules[0].name) if pricing_rules[0].mixed_conditions and doc: - stock_qty, amount = get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args) + stock_qty, amount, items = get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args) + pricing_rules[0].apply_rule_on_other_items = items elif pricing_rules[0].is_cumulative: items = [args.get(frappe.scrub(pr_doc.get('apply_on')))] @@ -282,7 +284,7 @@ def filter_pricing_rules_for_qty_amount(qty, rate, pricing_rules, args=None): status = True # if user has created item price against the transaction UOM - if rule.get("uom") == args.get("uom"): + if args and rule.get("uom") == args.get("uom"): conversion_factor = 1.0 if status and (flt(rate) >= (flt(rule.min_amt) * conversion_factor) @@ -339,17 +341,19 @@ def get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args): sum_qty += data[0] sum_amt += data[1] - return sum_qty, sum_amt + return sum_qty, sum_amt, items def get_qty_and_rate_for_other_item(doc, pr_doc, pricing_rules): - for d in get_pricing_rule_items(pr_doc): - for row in doc.items: - if d == row.get(frappe.scrub(pr_doc.apply_on)): - pricing_rules = filter_pricing_rules_for_qty_amount(row.get("stock_qty"), - row.get("amount"), pricing_rules, row) + items = get_pricing_rule_items(pr_doc) - if pricing_rules and pricing_rules[0]: - return pricing_rules + for row in doc.items: + if row.get(frappe.scrub(pr_doc.apply_rule_on_other)) in items: + pricing_rules = filter_pricing_rules_for_qty_amount(row.get("stock_qty"), + row.get("amount"), pricing_rules, row) + + if pricing_rules and pricing_rules[0]: + pricing_rules[0].apply_rule_on_other_items = items + return pricing_rules def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]): sum_qty, sum_amt = [0, 0] @@ -397,38 +401,15 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]): return [sum_qty, sum_amt] -def validate_pricing_rules(doc): - validate_pricing_rule_on_transactions(doc) - - for d in doc.items: - validate_pricing_rule_on_items(doc, d) - - doc.calculate_taxes_and_totals() - -def validate_pricing_rule_on_items(doc, item_row, do_not_validate = False): - value = 0 - for pricing_rule in get_applied_pricing_rules(doc, item_row): - pr_doc = frappe.get_doc('Pricing Rule', pricing_rule) - - if pr_doc.get('apply_on') == 'Transaction': continue - - if pr_doc.get('price_or_product_discount') == 'Product': - apply_pricing_rule_for_free_items(doc, pr_doc) - else: - for field in ['discount_percentage', 'discount_amount', 'rate']: - if not pr_doc.get(field): continue - - value += pr_doc.get(field) - apply_pricing_rule(doc, pr_doc, item_row, value, do_not_validate) - -def validate_pricing_rule_on_transactions(doc): +def apply_pricing_rule_on_transaction(doc): conditions = "apply_on = 'Transaction'" values = {} conditions = get_other_conditions(conditions, values, doc) pricing_rules = frappe.db.sql(""" Select `tabPricing Rule`.* from `tabPricing Rule` - where {conditions} """.format(conditions = conditions), values, as_dict=1) + where {conditions} and `tabPricing Rule`.disable = 0 + """.format(conditions = conditions), values, as_dict=1) if pricing_rules: pricing_rules = filter_pricing_rules_for_qty_amount(doc.total_qty, @@ -440,98 +421,83 @@ def validate_pricing_rule_on_transactions(doc): doc.set('apply_discount_on', d.apply_discount_on) for field in ['additional_discount_percentage', 'discount_amount']: - if not d.get(field): continue - pr_field = ('discount_percentage' if field == 'additional_discount_percentage' else field) + if not d.get(pr_field): continue + if d.validate_applied_rule and doc.get(field) < d.get(pr_field): frappe.msgprint(_("User has not applied rule on the invoice {0}") .format(doc.name)) else: doc.set(field, d.get(pr_field)) - elif d.price_or_product_discount == 'Product': - apply_pricing_rule_for_free_items(doc, d) -def get_applied_pricing_rules(doc, item_row): + doc.calculate_taxes_and_totals() + elif d.price_or_product_discount == 'Product': + item_details = frappe._dict({'parenttype': doc.doctype}) + get_product_discount_rule(d, item_details, doc) + apply_pricing_rule_for_free_items(doc, item_details.free_item_data) + doc.set_missing_values() + +def get_applied_pricing_rules(item_row): return (item_row.get("pricing_rules").split(',') if item_row.get("pricing_rules") else []) -def apply_pricing_rule_for_free_items(doc, pricing_rule): - if pricing_rule.get('free_item'): +def get_product_discount_rule(pricing_rule, item_details, doc=None): + free_item = (pricing_rule.free_item + if not pricing_rule.same_item or pricing_rule.apply_on == 'Transaction' else item_details.item_code) + + if not free_item: + frappe.throw(_("Free item not set in the pricing rule {0}") + .format(get_link_to_form("Pricing Rule", pricing_rule.name))) + + item_details.free_item_data = { + 'item_code': free_item, + 'qty': pricing_rule.free_qty or 1, + 'rate': pricing_rule.free_item_rate or 0, + 'price_list_rate': pricing_rule.free_item_rate or 0, + 'is_free_item': 1 + } + + item_data = frappe.get_cached_value('Item', free_item, ['item_name', + 'description', 'stock_uom'], as_dict=1) + + item_details.free_item_data.update(item_data) + item_details.free_item_data['uom'] = pricing_rule.free_item_uom or item_data.stock_uom + item_details.free_item_data['conversion_factor'] = get_conversion_factor(free_item, + item_details.free_item_data['uom']).get("conversion_factor", 1) + + if item_details.get("parenttype") == 'Purchase Order': + item_details.free_item_data['schedule_date'] = doc.schedule_date if doc else today() + + if item_details.get("parenttype") == 'Sales Order': + item_details.free_item_data['delivery_date'] = doc.delivery_date if doc else today() + +def apply_pricing_rule_for_free_items(doc, pricing_rule_args, set_missing_values=False): + if pricing_rule_args.get('item_code'): items = [d.item_code for d in doc.items - if d.item_code == (d.item_code - if pricing_rule.get('same_item') else pricing_rule.get('free_item')) and d.is_free_item] + if d.item_code == (pricing_rule_args.get("item_code")) and d.is_free_item] if not items: - doc.append('items', { - 'item_code': pricing_rule.get('free_item'), - 'qty': pricing_rule.get('free_qty'), - 'uom': pricing_rule.get('free_item_uom'), - 'rate': pricing_rule.get('free_item_rate'), - 'is_free_item': 1 - }) - - doc.set_missing_values() - -def apply_pricing_rule(doc, pr_doc, item_row, value, do_not_validate=False): - apply_on, items = get_apply_on_and_items(pr_doc, item_row) - - rule_applied = {} - - for item in doc.get("items"): - if item.get(apply_on) in items: - if not item.pricing_rules: - item.pricing_rules = item_row.pricing_rules - - for field in ['discount_percentage', 'discount_amount', 'rate']: - if not pr_doc.get(field): continue - - key = (item.name, item.pricing_rules) - if not pr_doc.validate_applied_rule: - rule_applied[key] = 1 - item.set(field, value) - elif item.get(field) < value: - if not do_not_validate and item.idx == item_row.idx: - rule_applied[key] = 0 - frappe.msgprint(_("Row {0}: user has not applied rule {1} on the item {2}") - .format(item.idx, pr_doc.title, item.item_code)) - - if rule_applied and doc.get("pricing_rules"): - for d in doc.get("pricing_rules"): - key = (d.child_docname, d.pricing_rule) - if key in rule_applied: - d.rule_applied = 1 - -def get_apply_on_and_items(pr_doc, item_row): - # for mixed or other items conditions - apply_on = frappe.scrub(pr_doc.get('apply_on')) - items = (get_pricing_rule_items(pr_doc) - if pr_doc.mixed_conditions else [item_row.get(apply_on)]) - - if pr_doc.apply_rule_on_other: - apply_on = frappe.scrub(pr_doc.apply_rule_on_other) - items = [pr_doc.get(apply_on)] - - return apply_on, items + doc.append('items', pricing_rule_args) def get_pricing_rule_items(pr_doc): + apply_on_data = [] apply_on = frappe.scrub(pr_doc.get('apply_on')) pricing_rule_apply_on = apply_on_table.get(pr_doc.get('apply_on')) - return [item.get(apply_on) for item in pr_doc.get(pricing_rule_apply_on)] or [] + for d in pr_doc.get(pricing_rule_apply_on): + if apply_on == 'item_group': + apply_on_data.extend(get_child_item_groups(d.get(apply_on))) + else: + apply_on_data.append(d.get(apply_on)) -@frappe.whitelist() -def validate_pricing_rule_for_different_cond(doc): - if isinstance(doc, string_types): - doc = json.loads(doc) + if pr_doc.apply_rule_on_other: + apply_on = frappe.scrub(pr_doc.apply_rule_on_other) + apply_on_data.append(pr_doc.get("other_" + apply_on)) - doc = frappe.get_doc(doc) - for d in doc.get("items"): - validate_pricing_rule_on_items(doc, d, True) - - return doc + return list(set(apply_on_data)) def validate_coupon_code(coupon_name): from frappe.utils import today,getdate diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index f4b656d3f68..c5c54837a7b 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -167,8 +167,15 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ make_comment_dialog_and_block_invoice: function(){ const me = this; - const title = __('Add Comment'); + const title = __('Block Invoice'); const fields = [ + { + fieldname: 'release_date', + read_only: 0, + fieldtype:'Date', + label: __('Release Date'), + default: me.frm.doc.release_date + }, { fieldname: 'hold_comment', read_only: 0, @@ -187,7 +194,11 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ const dialog_data = me.dialog.get_values(); frappe.call({ 'method': 'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.block_invoice', - 'args': {'name': me.frm.doc.name, 'hold_comment': dialog_data.hold_comment}, + 'args': { + 'name': me.frm.doc.name, + 'hold_comment': dialog_data.hold_comment, + 'release_date': dialog_data.release_date + }, 'callback': (r) => me.frm.reload_doc() }); me.dialog.hide(); @@ -330,23 +341,6 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({ frm: cur_frm }) }, - - asset: function(frm, cdt, cdn) { - var row = locals[cdt][cdn]; - if(row.asset) { - frappe.call({ - method: "erpnext.assets.doctype.asset_category.asset_category.get_asset_category_account", - args: { - "asset": row.asset, - "fieldname": "fixed_asset_account", - "account": row.expense_account - }, - callback: function(r, rt) { - frappe.model.set_value(cdt, cdn, "expense_account", r.message); - } - }) - } - } }); cur_frm.script_manager.make(erpnext.accounts.PurchaseInvoice); @@ -399,21 +393,11 @@ cur_frm.fields_dict['items'].grid.get_field("item_code").get_query = function(do cur_frm.fields_dict['credit_to'].get_query = function(doc) { // filter on Account - if (doc.supplier) { - return { - filters: { - 'account_type': 'Payable', - 'is_group': 0, - 'company': doc.company - } - } - } else { - return { - filters: { - 'report_type': 'Balance Sheet', - 'is_group': 0, - 'company': doc.company - } + return { + filters: { + 'account_type': 'Payable', + 'is_group': 0, + 'company': doc.company } } } @@ -430,19 +414,7 @@ cur_frm.fields_dict['select_print_heading'].get_query = function(doc, cdt, cdn) cur_frm.set_query("expense_account", "items", function(doc) { return { query: "erpnext.controllers.queries.get_expense_account", - filters: {'company': doc.company} - } -}); - -cur_frm.set_query("asset", "items", function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; - return { - filters: { - 'item_code': d.item_code, - 'docstatus': 1, - 'company': doc.company, - 'status': 'Submitted' - } + filters: {'company': doc.company } } }); diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 6fe18115c04..7725994c6b0 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "autoname": "naming_series:", "creation": "2013-05-21 16:16:39", @@ -417,6 +418,7 @@ "fieldname": "contact_email", "fieldtype": "Small Text", "label": "Contact Email", + "options": "Email", "print_hide": 1, "read_only": 1 }, @@ -705,7 +707,7 @@ }, { "fieldname": "other_charges_calculation", - "fieldtype": "Text", + "fieldtype": "Long Text", "label": "Taxes and Charges Calculation", "no_copy": 1, "oldfieldtype": "HTML", @@ -1287,7 +1289,8 @@ "icon": "fa fa-file-text", "idx": 204, "is_submittable": 1, - "modified": "2019-09-17 22:31:42.666601", + "links": [], + "modified": "2019-12-30 19:13:49.610538", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 9c1a9ece779..4002d7e55a8 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -18,7 +18,7 @@ from erpnext.accounts.general_ledger import make_gl_entries, merge_similar_entri from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt from erpnext.buying.utils import check_on_hold_or_closed_status from erpnext.accounts.general_ledger import get_round_off_account_and_cost_center -from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_disabled +from erpnext.assets.doctype.asset.asset import get_asset_account, is_cwip_accounting_enabled from frappe.model.mapper import get_mapped_doc from six import iteritems from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_party, update_linked_doc,\ @@ -98,7 +98,6 @@ class PurchaseInvoice(BuyingController): self.set_against_expense_account() self.validate_write_off_account() self.validate_multiple_billing("Purchase Receipt", "pr_detail", "amount", "items") - self.validate_fixed_asset() self.create_remarks() self.set_status() self.validate_purchase_receipt_if_update_stock() @@ -226,6 +225,8 @@ class PurchaseInvoice(BuyingController): # in case of auto inventory accounting, # expense account is always "Stock Received But Not Billed" for a stock item # except epening entry, drop-ship entry and fixed asset items + if item.item_code: + asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category") if auto_accounting_for_stock and item.item_code in stock_items \ and self.is_opening == 'No' and not item.is_fixed_asset \ @@ -236,12 +237,8 @@ class PurchaseInvoice(BuyingController): item.expense_account = warehouse_account[item.warehouse]["account"] else: item.expense_account = stock_not_billed_account - elif item.is_fixed_asset and is_cwip_accounting_disabled(): - if not item.asset: - frappe.throw(_("Row {0}: asset is required for item {1}") - .format(item.idx, item.item_code)) - - item.expense_account = get_asset_category_account(item.asset, 'fixed_asset_account', + elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category): + item.expense_account = get_asset_category_account('fixed_asset_account', item=item.item_code, company = self.company) elif item.is_fixed_asset and item.pr_detail: item.expense_account = asset_received_but_not_billed @@ -251,7 +248,7 @@ class PurchaseInvoice(BuyingController): def set_against_expense_account(self): against_accounts = [] for item in self.get("items"): - if item.expense_account not in against_accounts: + if item.expense_account and (item.expense_account not in against_accounts): against_accounts.append(item.expense_account) self.against_expense_account = ",".join(against_accounts) @@ -392,7 +389,8 @@ class PurchaseInvoice(BuyingController): self.make_supplier_gl_entry(gl_entries) self.make_item_gl_entries(gl_entries) - if not is_cwip_accounting_disabled(): + + if self.check_asset_cwip_enabled(): self.get_asset_gl_entry(gl_entries) self.make_tax_gl_entries(gl_entries) @@ -405,6 +403,15 @@ class PurchaseInvoice(BuyingController): return gl_entries + def check_asset_cwip_enabled(self): + # Check if there exists any item with cwip accounting enabled in it's asset category + for item in self.get("items"): + if item.item_code and item.is_fixed_asset: + asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category") + if is_cwip_accounting_enabled(asset_category): + return 1 + return 0 + def make_supplier_gl_entry(self, gl_entries): # Checked both rounding_adjustment and rounded_total # because rounded_total had value even before introcution of posting GLE based on rounded total @@ -445,9 +452,15 @@ class PurchaseInvoice(BuyingController): fields = ["voucher_detail_no", "stock_value_difference"], filters={'voucher_no': self.name}): voucher_wise_stock_value.setdefault(d.voucher_detail_no, d.stock_value_difference) + valuation_tax_accounts = [d.account_head for d in self.get("taxes") + if d.category in ('Valuation', 'Total and Valuation') + and flt(d.base_tax_amount_after_discount_amount)] + for item in self.get("items"): if flt(item.base_net_amount): account_currency = get_account_currency(item.expense_account) + if item.item_code: + asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category") if self.update_stock and self.auto_accounting_for_stock and item.item_code in stock_items: # warehouse account @@ -490,31 +503,61 @@ class PurchaseInvoice(BuyingController): "remarks": self.get("remarks") or _("Accounting Entry for Stock"), "credit": flt(item.rm_supp_cost) }, warehouse_account[self.supplier_warehouse]["account_currency"], item=item)) - elif not item.is_fixed_asset or (item.is_fixed_asset and is_cwip_accounting_disabled()): + elif not item.is_fixed_asset or (item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category)): expense_account = (item.expense_account if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account) - gl_entries.append( - self.get_gl_dict({ + if not item.is_fixed_asset: + amount = flt(item.base_net_amount, item.precision("base_net_amount")) + else: + amount = flt(item.base_net_amount + item.item_tax_amount, item.precision("base_net_amount")) + + gl_entries.append(self.get_gl_dict({ "account": expense_account, "against": self.supplier, - "debit": flt(item.base_net_amount, item.precision("base_net_amount")), - "debit_in_account_currency": (flt(item.base_net_amount, - item.precision("base_net_amount")) if account_currency==self.company_currency - else flt(item.net_amount, item.precision("net_amount"))), + "debit": amount, "cost_center": item.cost_center, "project": item.project - }, account_currency, item=item) - ) + }, account_currency, item=item)) + + # If asset is bought through this document and not linked to PR + if self.update_stock and item.landed_cost_voucher_amount: + expenses_included_in_asset_valuation = self.get_company_default("expenses_included_in_asset_valuation") + # Amount added through landed-cost-voucher + gl_entries.append(self.get_gl_dict({ + "account": expenses_included_in_asset_valuation, + "against": expense_account, + "cost_center": item.cost_center, + "remarks": self.get("remarks") or _("Accounting Entry for Stock"), + "credit": flt(item.landed_cost_voucher_amount), + "project": item.project + }, item=item)) + + gl_entries.append(self.get_gl_dict({ + "account": expense_account, + "against": expenses_included_in_asset_valuation, + "cost_center": item.cost_center, + "remarks": self.get("remarks") or _("Accounting Entry for Stock"), + "debit": flt(item.landed_cost_voucher_amount), + "project": item.project + }, item=item)) + + # update gross amount of asset bought through this document + assets = frappe.db.get_all('Asset', + filters={ 'purchase_invoice': self.name, 'item_code': item.item_code } + ) + for asset in assets: + frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate)) + frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)) if self.auto_accounting_for_stock and self.is_opening == "No" and \ item.item_code in stock_items and item.item_tax_amount: # Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt - if item.purchase_receipt: + if item.purchase_receipt and valuation_tax_accounts: negative_expense_booked_in_pr = frappe.db.sql("""select name from `tabGL Entry` - where voucher_type='Purchase Receipt' and voucher_no=%s and account=%s""", - (item.purchase_receipt, self.expenses_included_in_valuation)) + where voucher_type='Purchase Receipt' and voucher_no=%s and account in %s""", + (item.purchase_receipt, valuation_tax_accounts)) if not negative_expense_booked_in_pr: gl_entries.append( @@ -531,27 +574,27 @@ class PurchaseInvoice(BuyingController): item.precision("item_tax_amount")) def get_asset_gl_entry(self, gl_entries): + arbnb_account = self.get_company_default("asset_received_but_not_billed") + eiiav_account = self.get_company_default("expenses_included_in_asset_valuation") + for item in self.get("items"): if item.is_fixed_asset: - eiiav_account = self.get_company_default("expenses_included_in_asset_valuation") - asset_amount = flt(item.net_amount) + flt(item.item_tax_amount/self.conversion_rate) base_asset_amount = flt(item.base_net_amount + item.item_tax_amount) - if (not item.expense_account or frappe.db.get_value('Account', - item.expense_account, 'account_type') not in ['Asset Received But Not Billed', 'Fixed Asset']): - arbnb_account = self.get_company_default("asset_received_but_not_billed") + item_exp_acc_type = frappe.db.get_value('Account', item.expense_account, 'account_type') + if (not item.expense_account or item_exp_acc_type not in ['Asset Received But Not Billed', 'Fixed Asset']): item.expense_account = arbnb_account if not self.update_stock: - asset_rbnb_currency = get_account_currency(item.expense_account) + arbnb_currency = get_account_currency(item.expense_account) gl_entries.append(self.get_gl_dict({ "account": item.expense_account, "against": self.supplier, "remarks": self.get("remarks") or _("Accounting Entry for Asset"), "debit": base_asset_amount, "debit_in_account_currency": (base_asset_amount - if asset_rbnb_currency == self.company_currency else asset_amount), + if arbnb_currency == self.company_currency else asset_amount), "cost_center": item.cost_center }, item=item)) @@ -568,8 +611,7 @@ class PurchaseInvoice(BuyingController): item.item_tax_amount / self.conversion_rate) }, item=item)) else: - cwip_account = get_asset_account("capital_work_in_progress_account", - item.asset, company = self.company) + cwip_account = get_asset_account("capital_work_in_progress_account", company = self.company) cwip_account_currency = get_account_currency(cwip_account) gl_entries.append(self.get_gl_dict({ @@ -595,6 +637,36 @@ class PurchaseInvoice(BuyingController): item.item_tax_amount / self.conversion_rate) }, item=item)) + # When update stock is checked + # Assets are bought through this document then it will be linked to this document + if self.update_stock: + if flt(item.landed_cost_voucher_amount): + gl_entries.append(self.get_gl_dict({ + "account": eiiav_account, + "against": cwip_account, + "cost_center": item.cost_center, + "remarks": self.get("remarks") or _("Accounting Entry for Stock"), + "credit": flt(item.landed_cost_voucher_amount), + "project": item.project + }, item=item)) + + gl_entries.append(self.get_gl_dict({ + "account": cwip_account, + "against": eiiav_account, + "cost_center": item.cost_center, + "remarks": self.get("remarks") or _("Accounting Entry for Stock"), + "debit": flt(item.landed_cost_voucher_amount), + "project": item.project + }, item=item)) + + # update gross amount of assets bought through this document + assets = frappe.db.get_all('Asset', + filters={ 'purchase_invoice': self.name, 'item_code': item.item_code } + ) + for asset in assets: + frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate)) + frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)) + return gl_entries def make_stock_adjustment_entry(self, gl_entries, item, voucher_wise_stock_value, account_currency): @@ -645,14 +717,14 @@ class PurchaseInvoice(BuyingController): if account_currency==self.company_currency \ else tax.tax_amount_after_discount_amount, "cost_center": tax.cost_center - }, account_currency) + }, account_currency, item=tax) ) # accumulate valuation tax if self.is_opening == "No" and tax.category in ("Valuation", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount): if self.auto_accounting_for_stock and not tax.cost_center: frappe.throw(_("Cost Center is required in row {0} in Taxes table for type {1}").format(tax.idx, _(tax.category))) - valuation_tax.setdefault(tax.cost_center, 0) - valuation_tax[tax.cost_center] += \ + valuation_tax.setdefault(tax.name, 0) + valuation_tax[tax.name] += \ (tax.add_deduct_tax == "Add" and 1 or -1) * flt(tax.base_tax_amount_after_discount_amount) if self.is_opening == "No" and self.negative_expense_to_be_booked and valuation_tax: @@ -662,36 +734,38 @@ class PurchaseInvoice(BuyingController): total_valuation_amount = sum(valuation_tax.values()) amount_including_divisional_loss = self.negative_expense_to_be_booked i = 1 - for cost_center, amount in iteritems(valuation_tax): - if i == len(valuation_tax): - applicable_amount = amount_including_divisional_loss - else: - applicable_amount = self.negative_expense_to_be_booked * (amount / total_valuation_amount) - amount_including_divisional_loss -= applicable_amount + for tax in self.get("taxes"): + if valuation_tax.get(tax.name): + if i == len(valuation_tax): + applicable_amount = amount_including_divisional_loss + else: + applicable_amount = self.negative_expense_to_be_booked * (valuation_tax[tax.name] / total_valuation_amount) + amount_including_divisional_loss -= applicable_amount - gl_entries.append( - self.get_gl_dict({ - "account": self.expenses_included_in_valuation, - "cost_center": cost_center, - "against": self.supplier, - "credit": applicable_amount, - "remarks": self.remarks or "Accounting Entry for Stock" - }) - ) + gl_entries.append( + self.get_gl_dict({ + "account": tax.account_head, + "cost_center": tax.cost_center, + "against": self.supplier, + "credit": applicable_amount, + "remarks": self.remarks or _("Accounting Entry for Stock"), + }, item=tax) + ) - i += 1 + i += 1 if self.auto_accounting_for_stock and self.update_stock and valuation_tax: - for cost_center, amount in iteritems(valuation_tax): - gl_entries.append( - self.get_gl_dict({ - "account": self.expenses_included_in_valuation, - "cost_center": cost_center, - "against": self.supplier, - "credit": amount, - "remarks": self.remarks or "Accounting Entry for Stock" - }) - ) + for tax in self.get("taxes"): + if valuation_tax.get(tax.name): + gl_entries.append( + self.get_gl_dict({ + "account": tax.account_head, + "cost_center": tax.cost_center, + "against": self.supplier, + "credit": valuation_tax[tax.name], + "remarks": self.remarks or "Accounting Entry for Stock" + }, item=tax) + ) def make_payment_gl_entries(self, gl_entries): # Make Cash GL Entries @@ -756,7 +830,11 @@ class PurchaseInvoice(BuyingController): ) def make_gle_for_rounding_adjustment(self, gl_entries): - if self.rounding_adjustment: + # if rounding adjustment in small and conversion rate is also small then + # base_rounding_adjustment may become zero due to small precision + # eg: rounding_adjustment = 0.01 and exchange rate = 0.05 and precision of base_rounding_adjustment is 2 + # then base_rounding_adjustment becomes zero and error is thrown in GL Entry + if self.rounding_adjustment and self.base_rounding_adjustment: round_off_account, round_off_cost_center = \ get_round_off_account_and_cost_center(self.company) @@ -788,6 +866,7 @@ class PurchaseInvoice(BuyingController): # because updating ordered qty in bin depends upon updated ordered qty in PO if self.update_stock == 1: self.update_stock_ledger() + self.delete_auto_created_batches() self.make_gl_entries_on_cancel() self.update_project() @@ -829,7 +908,7 @@ class PurchaseInvoice(BuyingController): if pi: pi = pi[0][0] - frappe.throw(_("Supplier Invoice No exists in Purchase Invoice {0}".format(pi))) + frappe.throw(_("Supplier Invoice No exists in Purchase Invoice {0}").format(pi)) def update_billing_status_in_pr(self, update_modified=True): updated_pr = [] @@ -849,9 +928,10 @@ class PurchaseInvoice(BuyingController): def on_recurring(self, reference_doc, auto_repeat_doc): self.due_date = None - def block_invoice(self, hold_comment=None): + def block_invoice(self, hold_comment=None, release_date=None): self.db_set('on_hold', 1) self.db_set('hold_comment', cstr(hold_comment)) + self.db_set('release_date', release_date) def unblock_invoice(self): self.db_set('on_hold', 0) @@ -935,10 +1015,10 @@ def unblock_invoice(name): @frappe.whitelist() -def block_invoice(name, hold_comment): +def block_invoice(name, hold_comment, release_date): if frappe.db.exists('Purchase Invoice', name): pi = frappe.get_doc('Purchase Invoice', name) - pi.block_invoice(hold_comment) + pi.block_invoice(hold_comment, release_date) @frappe.whitelist() def make_inter_company_sales_invoice(source_name, target_doc=None): diff --git a/erpnext/accounts/doctype/purchase_invoice/regional/india.js b/erpnext/accounts/doctype/purchase_invoice/regional/india.js new file mode 100644 index 00000000000..81488a2c52a --- /dev/null +++ b/erpnext/accounts/doctype/purchase_invoice/regional/india.js @@ -0,0 +1,3 @@ +{% include "erpnext/regional/india/taxes.js" %} + +erpnext.setup_auto_gst_taxation('Purchase Invoice'); diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index b2ad4f4d512..e41ad428469 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -207,16 +207,17 @@ class TestPurchaseInvoice(unittest.TestCase): self.check_gle_for_pi(pi.name) def check_gle_for_pi(self, pi): - gl_entries = frappe.db.sql("""select account, debit, credit + gl_entries = frappe.db.sql("""select account, sum(debit) as debit, sum(credit) as credit from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s - order by account asc""", pi, as_dict=1) + group by account""", pi, as_dict=1) + self.assertTrue(gl_entries) expected_values = dict((d[0], d) for d in [ ["Creditors - TCP1", 0, 720], ["Stock Received But Not Billed - TCP1", 500.0, 0], - ["_Test Account Shipping Charges - TCP1", 100.0, 0], - ["_Test Account VAT - TCP1", 120.0, 0], + ["_Test Account Shipping Charges - TCP1", 100.0, 0.0], + ["_Test Account VAT - TCP1", 120.0, 0] ]) for i, gle in enumerate(gl_entries): diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index 3a19bb1b6bf..acb0398b5c0 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "hash", "creation": "2013-05-22 12:43:10", "doctype": "DocType", @@ -71,8 +72,8 @@ "expense_account", "col_break5", "is_fixed_asset", - "asset", "asset_location", + "asset_category", "deferred_expense_section", "deferred_expense_account", "service_stop_date", @@ -116,6 +117,8 @@ "fieldtype": "Column Break" }, { + "fetch_from": "item_code.item_name", + "fetch_if_empty": 1, "fieldname": "item_name", "fieldtype": "Data", "in_global_search": 1, @@ -414,6 +417,7 @@ "print_hide": 1 }, { + "depends_on": "eval:!doc.is_fixed_asset", "fieldname": "batch_no", "fieldtype": "Link", "label": "Batch No", @@ -425,12 +429,14 @@ "fieldtype": "Column Break" }, { + "depends_on": "eval:!doc.is_fixed_asset", "fieldname": "serial_no", "fieldtype": "Text", "label": "Serial No", "no_copy": 1 }, { + "depends_on": "eval:!doc.is_fixed_asset", "fieldname": "rejected_serial_no", "fieldtype": "Text", "label": "Rejected Serial No", @@ -502,7 +508,8 @@ "depends_on": "enable_deferred_expense", "fieldname": "service_stop_date", "fieldtype": "Date", - "label": "Service Stop Date" + "label": "Service Stop Date", + "no_copy": 1 }, { "default": "0", @@ -518,13 +525,15 @@ "depends_on": "enable_deferred_expense", "fieldname": "service_start_date", "fieldtype": "Date", - "label": "Service Start Date" + "label": "Service Start Date", + "no_copy": 1 }, { "depends_on": "enable_deferred_expense", "fieldname": "service_end_date", "fieldtype": "Date", - "label": "Service End Date" + "label": "Service End Date", + "no_copy": 1 }, { "fieldname": "reference", @@ -615,6 +624,7 @@ }, { "default": "0", + "fetch_from": "item_code.is_fixed_asset", "fieldname": "is_fixed_asset", "fieldtype": "Check", "hidden": 1, @@ -623,14 +633,6 @@ "print_hide": 1, "read_only": 1 }, - { - "depends_on": "is_fixed_asset", - "fieldname": "asset", - "fieldtype": "Link", - "label": "Asset", - "no_copy": 1, - "options": "Asset" - }, { "depends_on": "is_fixed_asset", "fieldname": "asset_location", @@ -676,7 +678,7 @@ "fieldname": "pr_detail", "fieldtype": "Data", "hidden": 1, - "label": "PR Detail", + "label": "Purchase Receipt Detail", "no_copy": 1, "oldfieldname": "pr_detail", "oldfieldtype": "Data", @@ -754,11 +756,22 @@ "fieldtype": "Data", "label": "Manufacturer Part Number", "read_only": 1 + }, + { + "depends_on": "is_fixed_asset", + "fetch_from": "item_code.asset_category", + "fieldname": "asset_category", + "fieldtype": "Data", + "in_preview": 1, + "label": "Asset Category", + "options": "Asset Category", + "read_only": 1 } ], "idx": 1, "istable": 1, - "modified": "2019-09-17 22:32:05.984240", + "links": [], + "modified": "2019-12-04 12:23:17.046413", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json index bc42630d474..a18fec61cf3 100644 --- a/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json +++ b/erpnext/accounts/doctype/purchase_taxes_and_charges_template/purchase_taxes_and_charges_template.json @@ -1,300 +1,108 @@ { - "allow_copy": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:title", - "beta": 0, - "creation": "2013-01-10 16:34:08", - "custom": 0, - "description": "Standard tax template that can be applied to all Purchase Transactions. This template can contain list of tax heads and also other expense heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Consider Tax or Charge for: In this section you can specify if the tax / charge is only for valuation (not a part of total) or only for total (does not add value to the item) or for both.\n10. Add or Deduct: Whether you want to add or deduct the tax.", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, + "allow_import": 1, + "allow_rename": 1, + "creation": "2013-01-10 16:34:08", + "description": "Standard tax template that can be applied to all Purchase Transactions. This template can contain list of tax heads and also other expense heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Consider Tax or Charge for: In this section you can specify if the tax / charge is only for valuation (not a part of total) or only for total (does not add value to the item) or for both.\n10. Add or Deduct: Whether you want to add or deduct the tax.", + "doctype": "DocType", + "document_type": "Setup", + "field_order": [ + "title", + "is_default", + "disabled", + "column_break4", + "company", + "tax_category", + "section_break6", + "taxes" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "title", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Title", - "length": 0, - "no_copy": 1, - "oldfieldname": "title", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "title", + "fieldtype": "Data", + "label": "Title", + "no_copy": 1, + "oldfieldname": "title", + "oldfieldtype": "Data", + "reqd": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "is_default", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Default", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "is_default", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Default" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "disabled", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Disabled", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Disabled" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "column_break4", + "fieldtype": "Column Break" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Company", + "options": "Company", + "remember_last_selected_value": 1, + "reqd": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break6", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "section_break6", + "fieldtype": "Section Break" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "taxes", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Purchase Taxes and Charges", - "length": 0, - "no_copy": 0, - "oldfieldname": "purchase_tax_details", - "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "taxes", + "fieldtype": "Table", + "label": "Purchase Taxes and Charges", + "oldfieldname": "purchase_tax_details", + "oldfieldtype": "Table", + "options": "Purchase Taxes and Charges" + }, + { + "fieldname": "tax_category", + "fieldtype": "Link", + "label": "Tax Category", + "options": "Tax Category" } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-money", - "idx": 1, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2016-11-07 05:18:44.095798", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Purchase Taxes and Charges Template", - "owner": "wasim@webnotestech.com", + ], + "icon": "fa fa-money", + "idx": 1, + "modified": "2019-11-25 13:05:26.220275", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Purchase Taxes and Charges Template", + "owner": "wasim@webnotestech.com", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "is_custom": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Purchase Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase Manager" + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "is_custom": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Purchase Master Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase Master Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "is_custom": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "Purchase User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "read": 1, + "role": "Purchase User" } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "sort_order": "DESC", - "track_seen": 0 + ], + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index ed45b2cc2c7..a48d2244893 100755 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -357,14 +357,11 @@ def get_customer_wise_price_list(): def get_bin_data(pos_profile): itemwise_bin_data = {} - cond = "1=1" + filters = { 'actual_qty': ['>', 0] } if pos_profile.get('warehouse'): - cond = "warehouse = %(warehouse)s" + filters.update({ 'warehouse': pos_profile.get('warehouse') }) - bin_data = frappe.db.sql(""" select item_code, warehouse, actual_qty from `tabBin` - where actual_qty > 0 and {cond}""".format(cond=cond), { - 'warehouse': frappe.db.escape(pos_profile.get('warehouse')) - }, as_dict=1) + bin_data = frappe.db.get_all('Bin', fields = ['item_code', 'warehouse', 'actual_qty'], filters=filters) for bins in bin_data: if bins.item_code not in itemwise_bin_data: @@ -550,11 +547,15 @@ def make_address(args, customer): def make_email_queue(email_queue): name_list = [] + for key, data in iteritems(email_queue): name = frappe.db.get_value('Sales Invoice', {'offline_pos_name': key}, 'name') + if not name: continue + data = json.loads(data) sender = frappe.session.user print_format = "POS Invoice" if not cint(frappe.db.get_value('Print Format', 'POS Invoice', 'disabled')) else None + attachments = [frappe.attach_print('Sales Invoice', name, print_format=print_format)] make(subject=data.get('subject'), content=data.get('content'), recipients=data.get('recipients'), diff --git a/erpnext/accounts/doctype/sales_invoice/regional/india.js b/erpnext/accounts/doctype/sales_invoice/regional/india.js index c8305e325f6..ba6c03b95f8 100644 --- a/erpnext/accounts/doctype/sales_invoice/regional/india.js +++ b/erpnext/accounts/doctype/sales_invoice/regional/india.js @@ -1,3 +1,7 @@ +{% include "erpnext/regional/india/taxes.js" %} + +erpnext.setup_auto_gst_taxation('Sales Invoice'); + frappe.ui.form.on("Sales Invoice", { setup: function(frm) { frm.set_query('transporter', function() { @@ -21,7 +25,7 @@ frappe.ui.form.on("Sales Invoice", { if(frm.doc.docstatus == 1 && !frm.is_dirty() && !frm.doc.is_return && !frm.doc.ewaybill) { - frm.add_custom_button('e-Way Bill JSON', () => { + frm.add_custom_button('E-Way Bill JSON', () => { var w = window.open( frappe.urllib.get_full_url( "/api/method/erpnext.regional.india.utils.generate_ewb_json?" @@ -32,7 +36,8 @@ frappe.ui.form.on("Sales Invoice", { if (!w) { frappe.msgprint(__("Please enable pop-ups")); return; } - }, __("Make")); + }, __("Create")); } } + }); diff --git a/erpnext/accounts/doctype/sales_invoice/regional/india_list.js b/erpnext/accounts/doctype/sales_invoice/regional/india_list.js index 66d74b4b06a..d17582769cc 100644 --- a/erpnext/accounts/doctype/sales_invoice/regional/india_list.js +++ b/erpnext/accounts/doctype/sales_invoice/regional/india_list.js @@ -12,7 +12,7 @@ frappe.listview_settings['Sales Invoice'].onload = function (doclist) { for (let doc of selected_docs) { if (doc.docstatus !== 1) { - frappe.throw(__("e-Way Bill JSON can only be generated from a submitted document")); + frappe.throw(__("E-Way Bill JSON can only be generated from a submitted document")); } } @@ -29,5 +29,5 @@ frappe.listview_settings['Sales Invoice'].onload = function (doclist) { }; - doclist.page.add_actions_menu_item(__('Generate e-Way Bill JSON'), action, false); + doclist.page.add_actions_menu_item(__('Generate E-Way Bill JSON'), action, false); }; \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 3c852106635..db6ac55a119 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -556,22 +556,11 @@ cur_frm.cscript.cost_center = function(doc, cdt, cdn) { } cur_frm.set_query("debit_to", function(doc) { - // filter on Account - if (doc.customer) { - return { - filters: { - 'account_type': 'Receivable', - 'is_group': 0, - 'company': doc.company - } - } - } else { - return { - filters: { - 'report_type': 'Balance Sheet', - 'is_group': 0, - 'company': doc.company - } + return { + filters: { + 'account_type': 'Receivable', + 'is_group': 0, + 'company': doc.company } } }); @@ -697,8 +686,8 @@ frappe.ui.form.on('Sales Invoice', { if (frm.doc.company) { frappe.call({ - method:"frappe.contacts.doctype.address.address.get_default_address", - args:{ doctype:'Company',name:frm.doc.company}, + method:"erpnext.setup.doctype.company.company.get_default_company_address", + args:{name:frm.doc.company, existing_address: frm.doc.company_address}, callback: function(r){ if (r.message){ frm.set_value("company_address",r.message) @@ -789,22 +778,21 @@ frappe.ui.form.on('Sales Invoice', { method: "frappe.client.get_value", args:{ doctype: "Patient", - filters: {"name": frm.doc.patient}, + filters: { + "name": frm.doc.patient + }, fieldname: "customer" }, - callback:function(patient_customer) { - if(patient_customer){ - frm.set_value("customer", patient_customer.message.customer); - frm.refresh_fields(); + callback:function(r) { + if(r && r.message.customer){ + frm.set_value("customer", r.message.customer); } } }); } - else{ - frm.set_value("customer", ''); - } } }, + refresh: function(frm) { if (frappe.boot.active_domains.includes("Healthcare")){ frm.set_df_property("patient", "hidden", 0); diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 96aceac8cd9..33ee7a2974e 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "autoname": "naming_series:", "creation": "2013-05-24 19:29:05", @@ -774,7 +775,7 @@ }, { "fieldname": "other_charges_calculation", - "fieldtype": "Text", + "fieldtype": "Long Text", "label": "Taxes and Charges Calculation", "no_copy": 1, "oldfieldtype": "HTML", @@ -1567,7 +1568,8 @@ "icon": "fa fa-file-text", "idx": 181, "is_submittable": 1, - "modified": "2019-10-05 21:39:49.235990", + "links": [], + "modified": "2019-12-30 19:15:59.580414", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 0ebca8bf51e..d8344ea6a6c 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -90,6 +90,7 @@ class SalesInvoice(SellingController): self.validate_account_for_change_amount() self.validate_fixed_asset() self.set_income_account_for_fixed_assets() + self.validate_item_cost_centers() validate_inter_company_party(self.doctype, self.customer, self.company, self.inter_company_invoice_reference) if cint(self.is_pos): @@ -136,6 +137,22 @@ class SalesInvoice(SellingController): if self.redeem_loyalty_points and self.loyalty_program and self.loyalty_points: validate_loyalty_points(self, self.loyalty_points) + def validate_fixed_asset(self): + for d in self.get("items"): + if d.is_fixed_asset and d.meta.get_field("asset") and d.asset: + asset = frappe.get_doc("Asset", d.asset) + if self.doctype == "Sales Invoice" and self.docstatus == 1: + if self.update_stock: + frappe.throw(_("'Update Stock' cannot be checked for fixed asset sale")) + + elif asset.status in ("Scrapped", "Cancelled", "Sold"): + frappe.throw(_("Row #{0}: Asset {1} cannot be submitted, it is already {2}").format(d.idx, d.asset, asset.status)) + + def validate_item_cost_centers(self): + for item in self.items: + cost_center_company = frappe.get_cached_value("Cost Center", item.cost_center, "company") + if cost_center_company != self.company: + frappe.throw(_("Row #{0}: Cost Center {1} does not belong to company {2}").format(frappe.bold(item.idx), frappe.bold(item.cost_center), frappe.bold(self.company))) def before_save(self): set_account_for_mode_of_payment(self) @@ -208,7 +225,7 @@ class SalesInvoice(SellingController): total_amount_in_payments += payment.amount invoice_total = self.rounded_total or self.grand_total if total_amount_in_payments < invoice_total: - frappe.throw(_("Total payments amount can't be greater than {}".format(-invoice_total))) + frappe.throw(_("Total payments amount can't be greater than {}").format(-invoice_total)) def validate_pos_paid_amount(self): if len(self.payments) == 0 and self.is_pos: @@ -338,7 +355,8 @@ class SalesInvoice(SellingController): "print_format": print_format, "allow_edit_rate": pos.get("allow_user_to_edit_rate"), "allow_edit_discount": pos.get("allow_user_to_edit_discount"), - "campaign": pos.get("campaign") + "campaign": pos.get("campaign"), + "allow_print_before_pay": pos.get("allow_print_before_pay") } def update_time_sheet(self, sales_invoice): @@ -402,6 +420,9 @@ class SalesInvoice(SellingController): if pos: self.allow_print_before_pay = pos.allow_print_before_pay + + if not for_validate: + self.tax_category = pos.get("tax_category") if not for_validate and not self.customer: self.customer = pos.customer @@ -525,9 +546,7 @@ class SalesInvoice(SellingController): for i in dic: if frappe.db.get_single_value('Selling Settings', dic[i][0]) == 'Yes': for d in self.get('items'): - is_stock_item = frappe.get_cached_value('Item', d.item_code, 'is_stock_item') - if (d.item_code and is_stock_item == 1\ - and not d.get(i.lower().replace(' ','_')) and not self.get(dic[i][1])): + if (d.item_code and not d.get(i.lower().replace(' ','_')) and not self.get(dic[i][1])): msgprint(_("{0} is mandatory for Item {1}").format(i,d.item_code), raise_exception=1) @@ -943,7 +962,7 @@ class SalesInvoice(SellingController): ) def make_gle_for_rounding_adjustment(self, gl_entries): - if flt(self.rounding_adjustment, self.precision("rounding_adjustment")): + if flt(self.rounding_adjustment, self.precision("rounding_adjustment")) and self.base_rounding_adjustment: round_off_account, round_off_cost_center = \ get_round_off_account_and_cost_center(self.company) @@ -991,10 +1010,8 @@ class SalesInvoice(SellingController): continue for serial_no in item.serial_no.split("\n"): - if serial_no and frappe.db.exists('Serial No', serial_no): - sno = frappe.get_doc('Serial No', serial_no) - sno.sales_invoice = invoice - sno.db_update() + if serial_no and frappe.db.get_value('Serial No', serial_no, 'item_code') == item.item_code: + frappe.db.set_value('Serial No', serial_no, 'sales_invoice', invoice) def validate_serial_numbers(self): """ @@ -1027,11 +1044,11 @@ class SalesInvoice(SellingController): si_serial_nos = set(get_serial_nos(serial_nos)) if si_serial_nos - dn_serial_nos: - frappe.throw(_("Serial Numbers in row {0} does not match with Delivery Note".format(item.idx))) + frappe.throw(_("Serial Numbers in row {0} does not match with Delivery Note").format(item.idx)) if item.serial_no and cint(item.qty) != len(si_serial_nos): - frappe.throw(_("Row {0}: {1} Serial numbers required for Item {2}. You have provided {3}.".format( - item.idx, item.qty, item.item_code, len(si_serial_nos)))) + frappe.throw(_("Row {0}: {1} Serial numbers required for Item {2}. You have provided {3}.").format( + item.idx, item.qty, item.item_code, len(si_serial_nos))) def validate_serial_against_sales_invoice(self): """ check if serial number is already used in other sales invoice """ @@ -1040,12 +1057,18 @@ class SalesInvoice(SellingController): continue for serial_no in item.serial_no.split("\n"): - sales_invoice = frappe.db.get_value("Serial No", serial_no, "sales_invoice") - if sales_invoice and self.name != sales_invoice: - sales_invoice_company = frappe.db.get_value("Sales Invoice", sales_invoice, "company") + serial_no_details = frappe.db.get_value("Serial No", serial_no, + ["sales_invoice", "item_code"], as_dict=1) + + if not serial_no_details: + continue + + if serial_no_details.sales_invoice and serial_no_details.item_code == item.item_code \ + and self.name != serial_no_details.sales_invoice: + sales_invoice_company = frappe.db.get_value("Sales Invoice", serial_no_details.sales_invoice, "company") if sales_invoice_company == self.company: - frappe.throw(_("Serial Number: {0} is already referenced in Sales Invoice: {1}" - .format(serial_no, sales_invoice))) + frappe.throw(_("Serial Number: {0} is already referenced in Sales Invoice: {1}") + .format(serial_no, serial_no_details.sales_invoice)) def update_project(self): if self.project: @@ -1218,24 +1241,27 @@ class SalesInvoice(SellingController): self.status = 'Draft' return + precision = self.precision("outstanding_amount") + outstanding_amount = flt(self.outstanding_amount, precision) + if not status: if self.docstatus == 2: status = "Cancelled" elif self.docstatus == 1: - if flt(self.outstanding_amount) > 0 and getdate(self.due_date) < getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed': + if outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed': self.status = "Overdue and Discounted" - elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) < getdate(nowdate()): + elif outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()): self.status = "Overdue" - elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed': + elif outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.is_discounted and self.get_discounting_status()=='Disbursed': self.status = "Unpaid and Discounted" - elif flt(self.outstanding_amount) > 0 and getdate(self.due_date) >= getdate(nowdate()): + elif outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()): self.status = "Unpaid" #Check if outstanding amount is 0 due to credit note issued against invoice - elif flt(self.outstanding_amount) <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}): + elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}): self.status = "Credit Note Issued" elif self.is_return == 1: self.status = "Return" - elif flt(self.outstanding_amount)<=0: + elif outstanding_amount <=0: self.status = "Paid" else: self.status = "Submitted" diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 530bd893c0e..a2a47b3a19c 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe import unittest, copy, time -from frappe.utils import nowdate, flt, getdate, cint +from frappe.utils import nowdate, flt, getdate, cint, add_days from frappe.model.dynamic_links import get_dynamic_link_map from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice @@ -1847,6 +1847,26 @@ class TestSalesInvoice(unittest.TestCase): self.assertEqual(data['billLists'][0]['vehicleNo'], 'KA12KA1234') self.assertEqual(data['billLists'][0]['itemList'][0]['taxableAmount'], 60000) + def test_item_tax_validity(self): + item = frappe.get_doc("Item", "_Test Item 2") + + if item.taxes: + item.taxes = [] + item.save() + + item.append("taxes", { + "item_tax_template": "_Test Item Tax Template 1", + "valid_from": add_days(nowdate(), 1) + }) + + item.save() + + sales_invoice = create_sales_invoice(item = "_Test Item 2", do_not_save=1) + sales_invoice.items[0].item_tax_template = "_Test Item Tax Template 1" + self.assertRaises(frappe.ValidationError, sales_invoice.save) + + item.taxes = [] + item.save() def create_sales_invoice(**args): si = frappe.new_doc("Sales Invoice") diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index 779ac4f656c..b2294e4318f 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "hash", "creation": "2013-06-04 11:02:19", "doctype": "DocType", @@ -484,7 +485,8 @@ "depends_on": "enable_deferred_revenue", "fieldname": "service_stop_date", "fieldtype": "Date", - "label": "Service Stop Date" + "label": "Service Stop Date", + "no_copy": 1 }, { "default": "0", @@ -500,13 +502,15 @@ "depends_on": "enable_deferred_revenue", "fieldname": "service_start_date", "fieldtype": "Date", - "label": "Service Start Date" + "label": "Service Start Date", + "no_copy": 1 }, { "depends_on": "enable_deferred_revenue", "fieldname": "service_end_date", "fieldtype": "Date", - "label": "Service End Date" + "label": "Service End Date", + "no_copy": 1 }, { "collapsible": 1, @@ -783,7 +787,8 @@ ], "idx": 1, "istable": 1, - "modified": "2019-07-16 16:36:46.527606", + "links": [], + "modified": "2019-12-04 12:22:38.517710", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json index 29e15d165fa..19781bdffaa 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json +++ b/erpnext/accounts/doctype/sales_taxes_and_charges_template/sales_taxes_and_charges_template.json @@ -1,299 +1,119 @@ { - "allow_copy": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:title", - "beta": 0, - "creation": "2013-01-10 16:34:09", - "custom": 0, - "description": "Standard tax template that can be applied to all Sales Transactions. This template can contain list of tax heads and also other expense / income heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Is this Tax included in Basic Rate?: If you check this, it means that this tax will not be shown below the item table, but will be included in the Basic Rate in your main item table. This is useful where you want give a flat price (inclusive of all taxes) price to customers.", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, + "allow_import": 1, + "allow_rename": 1, + "creation": "2013-01-10 16:34:09", + "description": "Standard tax template that can be applied to all Sales Transactions. This template can contain list of tax heads and also other expense / income heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Is this Tax included in Basic Rate?: If you check this, it means that this tax will not be shown below the item table, but will be included in the Basic Rate in your main item table. This is useful where you want give a flat price (inclusive of all taxes) price to customers.", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "title", + "is_default", + "disabled", + "column_break_3", + "company", + "tax_category", + "section_break_5", + "taxes" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "title", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Title", - "length": 0, - "no_copy": 1, - "oldfieldname": "title", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "title", + "fieldtype": "Data", + "label": "Title", + "no_copy": 1, + "oldfieldname": "title", + "oldfieldtype": "Data", + "reqd": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "is_default", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Default", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "is_default", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Default" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "disabled", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Disabled", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Company", - "length": 0, - "no_copy": 0, - "oldfieldname": "company", - "oldfieldtype": "Link", - "options": "Company", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Company", + "oldfieldname": "company", + "oldfieldtype": "Link", + "options": "Company", + "remember_last_selected_value": 1, + "reqd": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_5", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "section_break_5", + "fieldtype": "Section Break" + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "* Will be calculated in the transaction.", - "fieldname": "taxes", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sales Taxes and Charges", - "length": 0, - "no_copy": 0, - "oldfieldname": "other_charges", - "oldfieldtype": "Table", - "options": "Sales Taxes and Charges", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "description": "* Will be calculated in the transaction.", + "fieldname": "taxes", + "fieldtype": "Table", + "label": "Sales Taxes and Charges", + "oldfieldname": "other_charges", + "oldfieldtype": "Table", + "options": "Sales Taxes and Charges" + }, + { + "fieldname": "tax_category", + "fieldtype": "Link", + "label": "Tax Category", + "options": "Tax Category" } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-money", - "idx": 1, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2016-11-07 05:18:41.743257", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Sales Taxes and Charges Template", - "owner": "Administrator", + ], + "icon": "fa fa-money", + "idx": 1, + "modified": "2019-11-25 13:06:03.279099", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Sales Taxes and Charges Template", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 1, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "is_custom": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales User" + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "is_custom": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "is_custom": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales Master Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Master Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "sort_order": "ASC", - "track_seen": 0 + ], + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/share_transfer/share_transfer.json b/erpnext/accounts/doctype/share_transfer/share_transfer.json index f17bf04cafd..59a305317d8 100644 --- a/erpnext/accounts/doctype/share_transfer/share_transfer.json +++ b/erpnext/accounts/doctype/share_transfer/share_transfer.json @@ -188,7 +188,7 @@ } ], "is_submittable": 1, - "modified": "2019-11-07 13:31:17.999744", + "modified": "2019-12-20 14:48:01.990600", "modified_by": "Administrator", "module": "Accounts", "name": "Share Transfer", @@ -196,6 +196,7 @@ "permissions": [ { "amend": 1, + "cancel": 1, "create": 1, "delete": 1, "email": 1, @@ -221,6 +222,7 @@ "write": 1 }, { + "cancel": 1, "create": 1, "delete": 1, "email": 1, @@ -230,6 +232,7 @@ "report": 1, "role": "Accounts Manager", "share": 1, + "submit": 1, "write": 1 } ], diff --git a/erpnext/accounts/doctype/share_transfer/share_transfer.py b/erpnext/accounts/doctype/share_transfer/share_transfer.py index e95c69413f5..65f248e7bde 100644 --- a/erpnext/accounts/doctype/share_transfer/share_transfer.py +++ b/erpnext/accounts/doctype/share_transfer/share_transfer.py @@ -13,9 +13,9 @@ from frappe.utils import nowdate class ShareDontExists(ValidationError): pass class ShareTransfer(Document): - def before_submit(self): + def on_submit(self): if self.transfer_type == 'Issue': - shareholder = self.get_shareholder_doc(self.company) + shareholder = self.get_company_shareholder() shareholder.append('share_balance', { 'share_type': self.share_type, 'from_no': self.from_no, @@ -28,7 +28,7 @@ class ShareTransfer(Document): }) shareholder.save() - doc = frappe.get_doc('Shareholder', self.to_shareholder) + doc = self.get_shareholder_doc(self.to_shareholder) doc.append('share_balance', { 'share_type': self.share_type, 'from_no': self.from_no, @@ -41,11 +41,11 @@ class ShareTransfer(Document): elif self.transfer_type == 'Purchase': self.remove_shares(self.from_shareholder) - self.remove_shares(self.get_shareholder_doc(self.company).name) + self.remove_shares(self.get_company_shareholder().name) elif self.transfer_type == 'Transfer': self.remove_shares(self.from_shareholder) - doc = frappe.get_doc('Shareholder', self.to_shareholder) + doc = self.get_shareholder_doc(self.to_shareholder) doc.append('share_balance', { 'share_type': self.share_type, 'from_no': self.from_no, @@ -56,143 +56,127 @@ class ShareTransfer(Document): }) doc.save() + def on_cancel(self): + if self.transfer_type == 'Issue': + compnay_shareholder = self.get_company_shareholder() + self.remove_shares(compnay_shareholder.name) + self.remove_shares(self.to_shareholder) + + elif self.transfer_type == 'Purchase': + compnay_shareholder = self.get_company_shareholder() + from_shareholder = self.get_shareholder_doc(self.from_shareholder) + + from_shareholder.append('share_balance', { + 'share_type': self.share_type, + 'from_no': self.from_no, + 'to_no': self.to_no, + 'rate': self.rate, + 'amount': self.amount, + 'no_of_shares': self.no_of_shares + }) + + from_shareholder.save() + + compnay_shareholder.append('share_balance', { + 'share_type': self.share_type, + 'from_no': self.from_no, + 'to_no': self.to_no, + 'rate': self.rate, + 'amount': self.amount, + 'no_of_shares': self.no_of_shares + }) + + compnay_shareholder.save() + + elif self.transfer_type == 'Transfer': + self.remove_shares(self.to_shareholder) + from_shareholder = self.get_shareholder_doc(self.from_shareholder) + from_shareholder.append('share_balance', { + 'share_type': self.share_type, + 'from_no': self.from_no, + 'to_no': self.to_no, + 'rate': self.rate, + 'amount': self.amount, + 'no_of_shares': self.no_of_shares + }) + from_shareholder.save() + def validate(self): + self.get_company_shareholder() self.basic_validations() self.folio_no_validation() + if self.transfer_type == 'Issue': - if not self.get_shareholder_doc(self.company): - shareholder = frappe.get_doc({ - 'doctype': 'Shareholder', - 'title': self.company, - 'company': self.company, - 'is_company': 1 - }) - shareholder.insert() - # validate share doesnt exist in company - ret_val = self.share_exists(self.get_shareholder_doc(self.company).name) - if ret_val != False: + # validate share doesn't exist in company + ret_val = self.share_exists(self.get_company_shareholder().name) + if ret_val in ('Complete', 'Partial'): frappe.throw(_('The shares already exist'), frappe.DuplicateEntryError) else: # validate share exists with from_shareholder ret_val = self.share_exists(self.from_shareholder) - if ret_val != True: + if ret_val in ('Outside', 'Partial'): frappe.throw(_("The shares don't exist with the {0}") .format(self.from_shareholder), ShareDontExists) def basic_validations(self): if self.transfer_type == 'Purchase': self.to_shareholder = '' - if self.from_shareholder is None or self.from_shareholder is '': + if not self.from_shareholder: frappe.throw(_('The field From Shareholder cannot be blank')) - if self.from_folio_no is None or self.from_folio_no is '': + if not self.from_folio_no: self.to_folio_no = self.autoname_folio(self.to_shareholder) - if self.asset_account is None: + if not self.asset_account: frappe.throw(_('The field Asset Account cannot be blank')) elif (self.transfer_type == 'Issue'): self.from_shareholder = '' - if self.to_shareholder is None or self.to_shareholder == '': + if not self.to_shareholder: frappe.throw(_('The field To Shareholder cannot be blank')) - if self.to_folio_no is None or self.to_folio_no is '': + if not self.to_folio_no: self.to_folio_no = self.autoname_folio(self.to_shareholder) - if self.asset_account is None: + if not self.asset_account: frappe.throw(_('The field Asset Account cannot be blank')) else: - if self.from_shareholder is None or self.to_shareholder is None: + if not self.from_shareholder or not self.to_shareholder: frappe.throw(_('The fields From Shareholder and To Shareholder cannot be blank')) - if self.to_folio_no is None or self.to_folio_no is '': + if not self.to_folio_no: self.to_folio_no = self.autoname_folio(self.to_shareholder) - if self.equity_or_liability_account is None: + if not self.equity_or_liability_account: frappe.throw(_('The field Equity/Liability Account cannot be blank')) if self.from_shareholder == self.to_shareholder: frappe.throw(_('The seller and the buyer cannot be the same')) if self.no_of_shares != self.to_no - self.from_no + 1: frappe.throw(_('The number of shares and the share numbers are inconsistent')) - if self.amount is None: + if not self.amount: self.amount = self.rate * self.no_of_shares if self.amount != self.rate * self.no_of_shares: frappe.throw(_('There are inconsistencies between the rate, no of shares and the amount calculated')) def share_exists(self, shareholder): - # return True if exits, - # False if completely doesn't exist, - # 'partially exists' if partailly doesn't exist - ret_val = self.recursive_share_check(shareholder, self.share_type, - query = { - 'from_no': self.from_no, - 'to_no': self.to_no - } - ) - if all(boolean == True for boolean in ret_val): - return True - elif True in ret_val: - return 'partially exists' - else: - return False - - def recursive_share_check(self, shareholder, share_type, query): - # query = {'from_no': share_starting_no, 'to_no': share_ending_no} - # Recursive check if a given part of shares is held by the shareholder - # return a list containing True and False - # Eg. [True, False, True] - # All True implies its completely inside - # All False implies its completely outside - # A mix implies its partially inside/outside - does_share_exist = [] - doc = frappe.get_doc('Shareholder', shareholder) + doc = self.get_shareholder_doc(shareholder) for entry in doc.share_balance: - if entry.share_type != share_type or \ - entry.from_no > query['to_no'] or \ - entry.to_no < query['from_no']: + if entry.share_type != self.share_type or \ + entry.from_no > self.to_no or \ + entry.to_no < self.from_no: continue # since query lies outside bounds - elif entry.from_no <= query['from_no'] and entry.to_no >= query['to_no']: - return [True] # absolute truth! - elif entry.from_no >= query['from_no'] and entry.to_no <= query['to_no']: - # split and check - does_share_exist.extend(self.recursive_share_check(shareholder, - share_type, - { - 'from_no': query['from_no'], - 'to_no': entry.from_no - 1 - } - )) - does_share_exist.append(True) - does_share_exist.extend(self.recursive_share_check(shareholder, - share_type, - { - 'from_no': entry.to_no + 1, - 'to_no': query['to_no'] - } - )) - elif query['from_no'] <= entry.from_no <= query['to_no'] and entry.to_no >= query['to_no']: - does_share_exist.extend(self.recursive_share_check(shareholder, - share_type, - { - 'from_no': query['from_no'], - 'to_no': entry.from_no - 1 - } - )) - elif query['from_no'] <= entry.to_no <= query['to_no'] and entry.from_no <= query['from_no']: - does_share_exist.extend(self.recursive_share_check(shareholder, - share_type, - { - 'from_no': entry.to_no + 1, - 'to_no': query['to_no'] - } - )) + elif entry.from_no <= self.from_no and entry.to_no >= self.to_no: #both inside + return 'Complete' # absolute truth! + elif entry.from_no <= self.from_no <= self.to_no: + return 'Partial' + elif entry.from_no <= self.to_no <= entry.to_no: + return 'Partial' - does_share_exist.append(False) - return does_share_exist + return 'Outside' def folio_no_validation(self): shareholders = ['from_shareholder', 'to_shareholder'] shareholders = [shareholder for shareholder in shareholders if self.get(shareholder) is not ''] for shareholder in shareholders: - doc = frappe.get_doc('Shareholder', self.get(shareholder)) + doc = self.get_shareholder_doc(self.get(shareholder)) if doc.company != self.company: frappe.throw(_('The shareholder does not belong to this company')) - if doc.folio_no is '' or doc.folio_no is None: + if not doc.folio_no: doc.folio_no = self.from_folio_no \ - if (shareholder == 'from_shareholder') else self.to_folio_no; + if (shareholder == 'from_shareholder') else self.to_folio_no doc.save() else: if doc.folio_no and doc.folio_no != (self.from_folio_no if (shareholder == 'from_shareholder') else self.to_folio_no): @@ -200,24 +184,14 @@ class ShareTransfer(Document): def autoname_folio(self, shareholder, is_company=False): if is_company: - doc = self.get_shareholder_doc(shareholder) + doc = self.get_company_shareholder() else: - doc = frappe.get_doc('Shareholder' , shareholder) + doc = self.get_shareholder_doc(shareholder) doc.folio_no = make_autoname('FN.#####') doc.save() return doc.folio_no def remove_shares(self, shareholder): - self.iterative_share_removal(shareholder, self.share_type, - { - 'from_no': self.from_no, - 'to_no' : self.to_no - }, - rate = self.rate, - amount = self.amount - ) - - def iterative_share_removal(self, shareholder, share_type, query, rate, amount): # query = {'from_no': share_starting_no, 'to_no': share_ending_no} # Shares exist for sure # Iterate over all entries and modify entry if in entry @@ -227,31 +201,31 @@ class ShareTransfer(Document): for entry in current_entries: # use spaceage logic here - if entry.share_type != share_type or \ - entry.from_no > query['to_no'] or \ - entry.to_no < query['from_no']: + if entry.share_type != self.share_type or \ + entry.from_no > self.to_no or \ + entry.to_no < self.from_no: new_entries.append(entry) continue # since query lies outside bounds - elif entry.from_no <= query['from_no'] and entry.to_no >= query['to_no']: + elif entry.from_no <= self.from_no and entry.to_no >= self.to_no: #split - if entry.from_no == query['from_no']: - if entry.to_no == query['to_no']: + if entry.from_no == self.from_no: + if entry.to_no == self.to_no: pass #nothing to append else: - new_entries.append(self.return_share_balance_entry(query['to_no']+1, entry.to_no, entry.rate)) + new_entries.append(self.return_share_balance_entry(self.to_no+1, entry.to_no, entry.rate)) else: - if entry.to_no == query['to_no']: - new_entries.append(self.return_share_balance_entry(entry.from_no, query['from_no']-1, entry.rate)) + if entry.to_no == self.to_no: + new_entries.append(self.return_share_balance_entry(entry.from_no, self.from_no-1, entry.rate)) else: - new_entries.append(self.return_share_balance_entry(entry.from_no, query['from_no']-1, entry.rate)) - new_entries.append(self.return_share_balance_entry(query['to_no']+1, entry.to_no, entry.rate)) - elif entry.from_no >= query['from_no'] and entry.to_no <= query['to_no']: + new_entries.append(self.return_share_balance_entry(entry.from_no, self.from_no-1, entry.rate)) + new_entries.append(self.return_share_balance_entry(self.to_no+1, entry.to_no, entry.rate)) + elif entry.from_no >= self.from_no and entry.to_no <= self.to_no: # split and check pass #nothing to append - elif query['from_no'] <= entry.from_no <= query['to_no'] and entry.to_no >= query['to_no']: - new_entries.append(self.return_share_balance_entry(query['to_no']+1, entry.to_no, entry.rate)) - elif query['from_no'] <= entry.to_no <= query['to_no'] and entry.from_no <= query['from_no']: - new_entries.append(self.return_share_balance_entry(entry.from_no, query['from_no']-1, entry.rate)) + elif self.from_no <= entry.from_no <= self.to_no and entry.to_no >= self.to_no: + new_entries.append(self.return_share_balance_entry(self.to_no+1, entry.to_no, entry.rate)) + elif self.from_no <= entry.to_no <= self.to_no and entry.from_no <= self.from_no: + new_entries.append(self.return_share_balance_entry(entry.from_no, self.from_no-1, entry.rate)) else: new_entries.append(entry) @@ -272,16 +246,34 @@ class ShareTransfer(Document): } def get_shareholder_doc(self, shareholder): - # Get Shareholder doc based on the Shareholder title - doc = frappe.get_list('Shareholder', - filters = [ - ('Shareholder', 'title', '=', shareholder) - ] - ) - if len(doc) == 1: - return frappe.get_doc('Shareholder', doc[0]['name']) - else: #It will necessarily by 0 indicating it doesn't exist - return False + # Get Shareholder doc based on the Shareholder name + if shareholder: + query_filters = {'name': shareholder} + + name = frappe.db.get_value('Shareholder', {'name': shareholder}, 'name') + + return frappe.get_doc('Shareholder', name) + + def get_company_shareholder(self): + # Get company doc or create one if not present + company_shareholder = frappe.db.get_value('Shareholder', + { + 'company': self.company, + 'is_company': 1 + }, 'name') + + if company_shareholder: + return frappe.get_doc('Shareholder', company_shareholder) + else: + shareholder = frappe.get_doc({ + 'doctype': 'Shareholder', + 'title': self.company, + 'company': self.company, + 'is_company': 1 + }) + shareholder.insert() + + return shareholder @frappe.whitelist() def make_jv_entry( company, account, amount, payment_account,\ diff --git a/erpnext/accounts/doctype/share_transfer/test_share_transfer.py b/erpnext/accounts/doctype/share_transfer/test_share_transfer.py index 910dfd05dab..2ff9b02129f 100644 --- a/erpnext/accounts/doctype/share_transfer/test_share_transfer.py +++ b/erpnext/accounts/doctype/share_transfer/test_share_transfer.py @@ -15,73 +15,73 @@ class TestShareTransfer(unittest.TestCase): frappe.db.sql("delete from `tabShare Balance`") share_transfers = [ { - "doctype" : "Share Transfer", - "transfer_type" : "Issue", - "date" : "2018-01-01", - "to_shareholder" : "SH-00001", - "share_type" : "Equity", - "from_no" : 1, - "to_no" : 500, - "no_of_shares" : 500, - "rate" : 10, - "company" : "_Test Company", - "asset_account" : "Cash - _TC", + "doctype": "Share Transfer", + "transfer_type": "Issue", + "date": "2018-01-01", + "to_shareholder": "SH-00001", + "share_type": "Equity", + "from_no": 1, + "to_no": 500, + "no_of_shares": 500, + "rate": 10, + "company": "_Test Company", + "asset_account": "Cash - _TC", "equity_or_liability_account": "Creditors - _TC" }, { - "doctype" : "Share Transfer", - "transfer_type" : "Transfer", - "date" : "2018-01-02", - "from_shareholder" : "SH-00001", - "to_shareholder" : "SH-00002", - "share_type" : "Equity", - "from_no" : 101, - "to_no" : 200, - "no_of_shares" : 100, - "rate" : 15, - "company" : "_Test Company", + "doctype": "Share Transfer", + "transfer_type": "Transfer", + "date": "2018-01-02", + "from_shareholder": "SH-00001", + "to_shareholder": "SH-00002", + "share_type": "Equity", + "from_no": 101, + "to_no": 200, + "no_of_shares": 100, + "rate": 15, + "company": "_Test Company", "equity_or_liability_account": "Creditors - _TC" }, { - "doctype" : "Share Transfer", - "transfer_type" : "Transfer", - "date" : "2018-01-03", - "from_shareholder" : "SH-00001", - "to_shareholder" : "SH-00003", - "share_type" : "Equity", - "from_no" : 201, - "to_no" : 500, - "no_of_shares" : 300, - "rate" : 20, - "company" : "_Test Company", + "doctype": "Share Transfer", + "transfer_type": "Transfer", + "date": "2018-01-03", + "from_shareholder": "SH-00001", + "to_shareholder": "SH-00003", + "share_type": "Equity", + "from_no": 201, + "to_no": 500, + "no_of_shares": 300, + "rate": 20, + "company": "_Test Company", "equity_or_liability_account": "Creditors - _TC" }, { - "doctype" : "Share Transfer", - "transfer_type" : "Transfer", - "date" : "2018-01-04", - "from_shareholder" : "SH-00003", - "to_shareholder" : "SH-00002", - "share_type" : "Equity", - "from_no" : 201, - "to_no" : 400, - "no_of_shares" : 200, - "rate" : 15, - "company" : "_Test Company", + "doctype": "Share Transfer", + "transfer_type": "Transfer", + "date": "2018-01-04", + "from_shareholder": "SH-00003", + "to_shareholder": "SH-00002", + "share_type": "Equity", + "from_no": 201, + "to_no": 400, + "no_of_shares": 200, + "rate": 15, + "company": "_Test Company", "equity_or_liability_account": "Creditors - _TC" }, { - "doctype" : "Share Transfer", - "transfer_type" : "Purchase", - "date" : "2018-01-05", - "from_shareholder" : "SH-00003", - "share_type" : "Equity", - "from_no" : 401, - "to_no" : 500, - "no_of_shares" : 100, - "rate" : 25, - "company" : "_Test Company", - "asset_account" : "Cash - _TC", + "doctype": "Share Transfer", + "transfer_type": "Purchase", + "date": "2018-01-05", + "from_shareholder": "SH-00003", + "share_type": "Equity", + "from_no": 401, + "to_no": 500, + "no_of_shares": 100, + "rate": 25, + "company": "_Test Company", + "asset_account": "Cash - _TC", "equity_or_liability_account": "Creditors - _TC" } ] @@ -91,33 +91,33 @@ class TestShareTransfer(unittest.TestCase): def test_invalid_share_transfer(self): doc = frappe.get_doc({ - "doctype" : "Share Transfer", - "transfer_type" : "Transfer", - "date" : "2018-01-05", - "from_shareholder" : "SH-00003", - "to_shareholder" : "SH-00002", - "share_type" : "Equity", - "from_no" : 1, - "to_no" : 100, - "no_of_shares" : 100, - "rate" : 15, - "company" : "_Test Company", + "doctype": "Share Transfer", + "transfer_type": "Transfer", + "date": "2018-01-05", + "from_shareholder": "SH-00003", + "to_shareholder": "SH-00002", + "share_type": "Equity", + "from_no": 1, + "to_no": 100, + "no_of_shares": 100, + "rate": 15, + "company": "_Test Company", "equity_or_liability_account": "Creditors - _TC" }) self.assertRaises(ShareDontExists, doc.insert) doc = frappe.get_doc({ - "doctype" : "Share Transfer", - "transfer_type" : "Purchase", - "date" : "2018-01-02", - "from_shareholder" : "SH-00001", - "share_type" : "Equity", - "from_no" : 1, - "to_no" : 200, - "no_of_shares" : 200, - "rate" : 15, - "company" : "_Test Company", - "asset_account" : "Cash - _TC", + "doctype": "Share Transfer", + "transfer_type": "Purchase", + "date": "2018-01-02", + "from_shareholder": "SH-00001", + "share_type": "Equity", + "from_no": 1, + "to_no": 200, + "no_of_shares": 200, + "rate": 15, + "company": "_Test Company", + "asset_account": "Cash - _TC", "equity_or_liability_account": "Creditors - _TC" }) self.assertRaises(ShareDontExists, doc.insert) diff --git a/erpnext/accounts/doctype/shareholder/shareholder.json b/erpnext/accounts/doctype/shareholder/shareholder.json index 873a3e76a3f..e94aea94b75 100644 --- a/erpnext/accounts/doctype/shareholder/shareholder.json +++ b/erpnext/accounts/doctype/shareholder/shareholder.json @@ -1,587 +1,163 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2017-12-25 16:50:53.878430", - "custom": 0, - "description": "", - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "autoname": "naming_series:", + "creation": "2017-12-25 16:50:53.878430", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "title", + "column_break_2", + "naming_series", + "section_break_2", + "folio_no", + "column_break_4", + "company", + "is_company", + "address_contacts", + "address_html", + "column_break_9", + "contact_html", + "section_break_3", + "share_balance", + "contact_list" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "title", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Title", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "title", + "fieldtype": "Data", + "label": "Title", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "options": "ACC-SH-.YYYY.-", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "options": "ACC-SH-.YYYY.-" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_2", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_2", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "folio_no", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Folio no.", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "fieldname": "folio_no", + "fieldtype": "Data", + "label": "Folio no.", + "read_only": 1, "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "is_company", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Company", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "is_company", + "fieldtype": "Check", + "hidden": 1, + "label": "Is Company", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "address_contacts", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Address and Contacts", - "length": 0, - "no_copy": 0, - "options": "fa fa-map-marker", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "address_contacts", + "fieldtype": "Section Break", + "label": "Address and Contacts", + "options": "fa fa-map-marker" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "address_html", - "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Address HTML", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "address_html", + "fieldtype": "HTML", + "label": "Address HTML", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_9", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "contact_html", - "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Contact HTML", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "contact_html", + "fieldtype": "HTML", + "label": "Contact HTML", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_3", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Share Balance", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_3", + "fieldtype": "Section Break", + "label": "Share Balance" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "share_balance", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Share Balance", - "length": 0, - "no_copy": 0, - "options": "Share Balance", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "share_balance", + "fieldtype": "Table", + "label": "Share Balance", + "options": "Share Balance", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Hidden list maintaining the list of contacts linked to Shareholder", - "fieldname": "contact_list", - "fieldtype": "Code", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Contact List", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "description": "Hidden list maintaining the list of contacts linked to Shareholder", + "fieldname": "contact_list", + "fieldtype": "Code", + "hidden": 1, + "label": "Contact List", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-09-18 14:14:24.953014", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Shareholder", - "name_case": "Title Case", - "owner": "Administrator", + ], + "modified": "2019-11-17 23:24:11.395882", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Shareholder", + "name_case": "Title Case", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "folio_no", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "title", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "search_fields": "folio_no", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "title", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py index a20f5c08726..1fd340c7316 100644 --- a/erpnext/accounts/doctype/shipping_rule/shipping_rule.py +++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule.py @@ -70,7 +70,7 @@ class ShippingRule(Document): def get_shipping_amount_from_rules(self, value): for condition in self.get("conditions"): - if not condition.to_value or (flt(condition.from_value) <= value <= flt(condition.to_value)): + if not condition.to_value or (flt(condition.from_value) <= flt(value) <= flt(condition.to_value)): return condition.shipping_amount return 0.0 @@ -82,7 +82,7 @@ class ShippingRule(Document): if not shipping_country: frappe.throw(_('Shipping Address does not have country, which is required for this Shipping Rule')) if shipping_country not in [d.country for d in self.countries]: - frappe.throw(_('Shipping rule not applicable for country {0}'.format(shipping_country))) + frappe.throw(_('Shipping rule not applicable for country {0}').format(shipping_country)) def add_shipping_rule_to_tax_table(self, doc, shipping_amount): shipping_charge = { diff --git a/erpnext/accounts/doctype/subscription/subscription.json b/erpnext/accounts/doctype/subscription/subscription.json index 29cb62a3972..32b97ba80b5 100644 --- a/erpnext/accounts/doctype/subscription/subscription.json +++ b/erpnext/accounts/doctype/subscription/subscription.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "ACC-SUB-.YYYY.-.#####", "creation": "2017-07-18 17:50:43.967266", "doctype": "DocType", @@ -155,7 +156,7 @@ "fieldname": "apply_additional_discount", "fieldtype": "Select", "label": "Apply Additional Discount On", - "options": "\nGrand Total\nNet total" + "options": "\nGrand Total\nNet Total" }, { "fieldname": "cb_2", @@ -196,7 +197,8 @@ "fieldtype": "Column Break" } ], - "modified": "2019-07-25 18:45:38.579579", + "links": [], + "modified": "2020-01-27 14:37:32.845173", "modified_by": "Administrator", "module": "Accounts", "name": "Subscription", diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index f13ca4c49e8..0933c7e8b84 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -195,7 +195,7 @@ class Subscription(Document): doc = frappe.get_doc('Sales Invoice', current.invoice) return doc else: - frappe.throw(_('Invoice {0} no longer exists'.format(current.invoice))) + frappe.throw(_('Invoice {0} no longer exists').format(current.invoice)) def is_new_subscription(self): """ @@ -280,7 +280,7 @@ class Subscription(Document): if self.additional_discount_percentage or self.additional_discount_amount: discount_on = self.apply_additional_discount - invoice.apply_additional_discount = discount_on if discount_on else 'Grand Total' + invoice.apply_discount_on = discount_on if discount_on else 'Grand Total' # Subscription period invoice.from_date = self.current_invoice_start @@ -339,6 +339,16 @@ class Subscription(Document): # Check invoice dates and make sure it doesn't have outstanding invoices return getdate(nowdate()) >= getdate(self.current_invoice_start) and not self.has_outstanding_invoice() + def is_current_invoice_paid(self): + if self.is_new_subscription(): + return False + + last_invoice = frappe.get_doc('Sales Invoice', self.invoices[-1].invoice) + if getdate(last_invoice.posting_date) == getdate(self.current_invoice_start) and last_invoice.status == 'Paid': + return True + + return False + def process_for_active(self): """ Called by `process` if the status of the `Subscription` is 'Active'. @@ -348,7 +358,7 @@ class Subscription(Document): 2. Change the `Subscription` status to 'Past Due Date' 3. Change the `Subscription` status to 'Cancelled' """ - if self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice(): + if not self.is_current_invoice_paid() and (self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice()): self.generate_invoice() if self.current_invoice_is_past_due(): self.status = 'Past Due Date' @@ -378,7 +388,7 @@ class Subscription(Document): """ current_invoice = self.get_current_invoice() if not current_invoice: - frappe.throw(_('Current invoice {0} is missing'.format(current_invoice.invoice))) + frappe.throw(_('Current invoice {0} is missing').format(current_invoice.invoice)) else: if self.is_not_outstanding(current_invoice): self.status = 'Active' diff --git a/erpnext/accounts/doctype/tax_category/tax_category.json b/erpnext/accounts/doctype/tax_category/tax_category.json index 3556e127675..1e3ae455b37 100644 --- a/erpnext/accounts/doctype/tax_category/tax_category.json +++ b/erpnext/accounts/doctype/tax_category/tax_category.json @@ -1,134 +1,134 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "field:title", - "beta": 0, - "creation": "2018-11-22 23:38:39.668804", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "allow_copy": 0, + "allow_events_in_timeline": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "field:title", + "beta": 0, + "creation": "2018-11-22 23:38:39.668804", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "title", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Title", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "title", + "fieldtype": "Data", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Title", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, "unique": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-11-22 23:38:39.668804", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Tax Category", - "name_case": "", - "owner": "Administrator", + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 0, + "max_attachments": 0, + "modified": "2020-01-15 17:14:28.951793", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Tax Category", + "name_case": "", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "amend": 0, + "cancel": 0, + "create": 0, + "delete": 0, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "set_user_permissions": 0, + "share": 1, + "submit": 0, "write": 0 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0, "track_views": 0 -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/tax_rule/tax_rule.py b/erpnext/accounts/doctype/tax_rule/tax_rule.py index ff87ba36b45..e4ebc6d12f9 100644 --- a/erpnext/accounts/doctype/tax_rule/tax_rule.py +++ b/erpnext/accounts/doctype/tax_rule/tax_rule.py @@ -95,7 +95,7 @@ class TaxRule(Document): if tax_rule: if tax_rule[0].priority == self.priority: - frappe.throw(_("Tax Rule Conflicts with {0}".format(tax_rule[0].name)), ConflictingTaxRule) + frappe.throw(_("Tax Rule Conflicts with {0}").format(tax_rule[0].name), ConflictingTaxRule) def validate_use_for_shopping_cart(self): '''If shopping cart is enabled and no tax rule exists for shopping cart, enable this one''' diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 43d9ad6435a..bb1b7e392dc 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -90,8 +90,12 @@ def merge_similar_entries(gl_map): else: merged_gl_map.append(entry) + company = gl_map[0].company if gl_map else erpnext.get_default_company() + company_currency = erpnext.get_company_currency(company) + precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), company_currency) + # filter zero debit and credit entries - merged_gl_map = filter(lambda x: flt(x.debit, 9)!=0 or flt(x.credit, 9)!=0, merged_gl_map) + merged_gl_map = filter(lambda x: flt(x.debit, precision)!=0 or flt(x.credit, precision)!=0, merged_gl_map) merged_gl_map = list(merged_gl_map) return merged_gl_map @@ -162,20 +166,46 @@ def validate_account_for_perpetual_inventory(gl_map): frappe.throw(_("Account: {0} can only be updated via Stock Transactions") .format(account), StockAccountInvalidTransaction) - elif account_bal != stock_bal: - frappe.throw(_("Account Balance ({0}) and Stock Value ({1}) is out of sync for account {2} and linked warehouse ({3}). Please create adjustment Journal Entry for amount {4}.") - .format(account_bal, stock_bal, account, comma_and(warehouse_list), stock_bal - account_bal), - StockValueAndAccountBalanceOutOfSync) + # This has been comment for a temporary, will add this code again on release of immutable ledger + # elif account_bal != stock_bal: + # precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), + # currency=frappe.get_cached_value('Company', gl_map[0].company, "default_currency")) + + # diff = flt(stock_bal - account_bal, precision) + # error_reason = _("Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses.").format( + # stock_bal, account_bal, frappe.bold(account)) + # error_resolution = _("Please create adjustment Journal Entry for amount {0} ").format(frappe.bold(diff)) + # stock_adjustment_account = frappe.db.get_value("Company",gl_map[0].company,"stock_adjustment_account") + + # db_or_cr_warehouse_account =('credit_in_account_currency' if diff < 0 else 'debit_in_account_currency') + # db_or_cr_stock_adjustment_account = ('debit_in_account_currency' if diff < 0 else 'credit_in_account_currency') + + # journal_entry_args = { + # 'accounts':[ + # {'account': account, db_or_cr_warehouse_account : abs(diff)}, + # {'account': stock_adjustment_account, db_or_cr_stock_adjustment_account : abs(diff) }] + # } + + # frappe.msgprint(msg="""{0}

{1}

""".format(error_reason, error_resolution), + # raise_exception=StockValueAndAccountBalanceOutOfSync, + # title=_('Values Out Of Sync'), + # primary_action={ + # 'label': _('Make Journal Entry'), + # 'client_action': 'erpnext.route_to_adjustment_jv', + # 'args': journal_entry_args + # }) def validate_cwip_accounts(gl_map): - if not cint(frappe.db.get_value("Asset Settings", None, "disable_cwip_accounting")) \ - and gl_map[0].voucher_type == "Journal Entry": + cwip_enabled = any([cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category","enable_cwip_accounting")]) + + if cwip_enabled and gl_map[0].voucher_type == "Journal Entry": cwip_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount where account_type = 'Capital Work in Progress' and is_group=0""")] for entry in gl_map: if entry.account in cwip_accounts: - frappe.throw(_("Account: {0} is capital Work in progress and can not be updated by Journal Entry").format(entry.account)) + frappe.throw( + _("Account: {0} is capital Work in progress and can not be updated by Journal Entry").format(entry.account)) def round_off_debit_credit(gl_map): precision = get_field_precision(frappe.get_meta("GL Entry").get_field("debit"), diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js index 854b973beae..efc76f9158b 100644 --- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js +++ b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js @@ -529,9 +529,16 @@ erpnext.accounts.ReconciliationRow = class ReconciliationRow { frappe.db.get_doc(dt, event.value) .then(doc => { let displayed_docs = [] + let payment = [] if (dt === "Payment Entry") { payment.currency = doc.payment_type == "Receive" ? doc.paid_to_account_currency : doc.paid_from_account_currency; payment.doctype = dt + payment.posting_date = doc.posting_date; + payment.party = doc.party; + payment.reference_no = doc.reference_no; + payment.reference_date = doc.reference_date; + payment.paid_amount = doc.paid_amount; + payment.name = doc.name; displayed_docs.push(payment); } else if (dt === "Journal Entry") { doc.accounts.forEach(payment => { @@ -564,8 +571,8 @@ erpnext.accounts.ReconciliationRow = class ReconciliationRow { const details_wrapper = me.dialog.fields_dict.payment_details.$wrapper; details_wrapper.append(frappe.render_template("linked_payment_header")); - displayed_docs.forEach(values => { - details_wrapper.append(frappe.render_template("linked_payment_row", values)); + displayed_docs.forEach(payment => { + details_wrapper.append(frappe.render_template("linked_payment_row", payment)); }) }) } diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py index bd4b4d7e0b1..69f9907a8d8 100644 --- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py +++ b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.py @@ -18,6 +18,10 @@ def reconcile(bank_transaction, payment_doctype, payment_name): account = frappe.db.get_value("Bank Account", transaction.bank_account, "account") gl_entry = frappe.get_doc("GL Entry", dict(account=account, voucher_type=payment_doctype, voucher_no=payment_name)) + if payment_doctype == "Payment Entry" and payment_entry.unallocated_amount > transaction.unallocated_amount: + frappe.throw(_("The unallocated amount of Payment Entry {0} \ + is greater than the Bank Transaction's unallocated amount").format(payment_name)) + if transaction.unallocated_amount == 0: frappe.throw(_("This bank transaction is already fully reconciled")) @@ -373,4 +377,4 @@ def sales_invoices_query(doctype, txt, searchfield, start, page_len, filters): 'start': start, 'page_len': page_len } - ) \ No newline at end of file + ) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 59936d5116d..156f2181b87 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -23,7 +23,7 @@ class DuplicatePartyAccountError(frappe.ValidationError): pass @frappe.whitelist() def get_party_details(party=None, account=None, party_type="Customer", company=None, posting_date=None, bill_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False, fetch_payment_terms_template=True, - party_address=None, shipping_address=None, pos_profile=None): + party_address=None, company_address=None, shipping_address=None, pos_profile=None): if not party: return {} @@ -31,14 +31,14 @@ def get_party_details(party=None, account=None, party_type="Customer", company=N frappe.throw(_("{0}: {1} does not exists").format(party_type, party)) return _get_party_details(party, account, party_type, company, posting_date, bill_date, price_list, currency, doctype, ignore_permissions, - fetch_payment_terms_template, party_address, shipping_address, pos_profile) + fetch_payment_terms_template, party_address, company_address, shipping_address, pos_profile) def _get_party_details(party=None, account=None, party_type="Customer", company=None, posting_date=None, bill_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False, - fetch_payment_terms_template=True, party_address=None, shipping_address=None, pos_profile=None): + fetch_payment_terms_template=True, party_address=None, company_address=None,shipping_address=None, pos_profile=None): - out = frappe._dict(set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype)) - party = out[party_type.lower()] + party_details = frappe._dict(set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype)) + party = party_details[party_type.lower()] if not ignore_permissions and not frappe.has_permission(party_type, "read", party): frappe.throw(_("Not permitted for {0}").format(party), frappe.PermissionError) @@ -46,76 +46,81 @@ def _get_party_details(party=None, account=None, party_type="Customer", company= party = frappe.get_doc(party_type, party) currency = party.default_currency if party.get("default_currency") else get_company_currency(company) - party_address, shipping_address = set_address_details(out, party, party_type, doctype, company, party_address, shipping_address) - set_contact_details(out, party, party_type) - set_other_values(out, party, party_type) - set_price_list(out, party, party_type, price_list, pos_profile) + party_address, shipping_address = set_address_details(party_details, party, party_type, doctype, company, party_address, company_address, shipping_address) + set_contact_details(party_details, party, party_type) + set_other_values(party_details, party, party_type) + set_price_list(party_details, party, party_type, price_list, pos_profile) - out["tax_category"] = get_address_tax_category(party.get("tax_category"), + party_details["tax_category"] = get_address_tax_category(party.get("tax_category"), party_address, shipping_address if party_type != "Supplier" else party_address) - out["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company, - customer_group=out.customer_group, supplier_group=out.supplier_group, tax_category=out.tax_category, - billing_address=party_address, shipping_address=shipping_address) + + if not party_details.get("taxes_and_charges"): + party_details["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company, + customer_group=party_details.customer_group, supplier_group=party_details.supplier_group, tax_category=party_details.tax_category, + billing_address=party_address, shipping_address=shipping_address) if fetch_payment_terms_template: - out["payment_terms_template"] = get_pyt_term_template(party.name, party_type, company) + party_details["payment_terms_template"] = get_pyt_term_template(party.name, party_type, company) - if not out.get("currency"): - out["currency"] = currency + if not party_details.get("currency"): + party_details["currency"] = currency # sales team if party_type=="Customer": - out["sales_team"] = [{ + party_details["sales_team"] = [{ "sales_person": d.sales_person, "allocated_percentage": d.allocated_percentage or None } for d in party.get("sales_team")] # supplier tax withholding category if party_type == "Supplier" and party: - out["supplier_tds"] = frappe.get_value(party_type, party.name, "tax_withholding_category") + party_details["supplier_tds"] = frappe.get_value(party_type, party.name, "tax_withholding_category") - return out + return party_details -def set_address_details(out, party, party_type, doctype=None, company=None, party_address=None, shipping_address=None): +def set_address_details(party_details, party, party_type, doctype=None, company=None, party_address=None, company_address=None, shipping_address=None): billing_address_field = "customer_address" if party_type == "Lead" \ else party_type.lower() + "_address" - out[billing_address_field] = party_address or get_default_address(party_type, party.name) + party_details[billing_address_field] = party_address or get_default_address(party_type, party.name) if doctype: - out.update(get_fetch_values(doctype, billing_address_field, out[billing_address_field])) + party_details.update(get_fetch_values(doctype, billing_address_field, party_details[billing_address_field])) # address display - out.address_display = get_address_display(out[billing_address_field]) + party_details.address_display = get_address_display(party_details[billing_address_field]) # shipping address if party_type in ["Customer", "Lead"]: - out.shipping_address_name = shipping_address or get_party_shipping_address(party_type, party.name) - out.shipping_address = get_address_display(out["shipping_address_name"]) + party_details.shipping_address_name = shipping_address or get_party_shipping_address(party_type, party.name) + party_details.shipping_address = get_address_display(party_details["shipping_address_name"]) if doctype: - out.update(get_fetch_values(doctype, 'shipping_address_name', out.shipping_address_name)) + party_details.update(get_fetch_values(doctype, 'shipping_address_name', party_details.shipping_address_name)) - if doctype and doctype in ['Delivery Note', 'Sales Invoice']: - out.update(get_company_address(company)) - if out.company_address: - out.update(get_fetch_values(doctype, 'company_address', out.company_address)) - get_regional_address_details(out, doctype, company) + if company_address: + party_details.update({'company_address': company_address}) + else: + party_details.update(get_company_address(company)) - elif doctype and doctype == "Purchase Invoice": - out.update(get_company_address(company)) - if out.company_address: - out["shipping_address"] = shipping_address or out["company_address"] - out.shipping_address_display = get_address_display(out["shipping_address"]) - out.update(get_fetch_values(doctype, 'shipping_address', out.shipping_address)) - get_regional_address_details(out, doctype, company) + if doctype and doctype in ['Delivery Note', 'Sales Invoice', 'Sales Order']: + if party_details.company_address: + party_details.update(get_fetch_values(doctype, 'company_address', party_details.company_address)) + get_regional_address_details(party_details, doctype, company) - return out.get(billing_address_field), out.shipping_address_name + elif doctype and doctype in ["Purchase Invoice", "Purchase Order", "Purchase Receipt"]: + if party_details.company_address: + party_details["shipping_address"] = shipping_address or party_details["company_address"] + party_details.shipping_address_display = get_address_display(party_details["shipping_address"]) + party_details.update(get_fetch_values(doctype, 'shipping_address', party_details.shipping_address)) + get_regional_address_details(party_details, doctype, company) + + return party_details.get(billing_address_field), party_details.shipping_address_name @erpnext.allow_regional -def get_regional_address_details(out, doctype, company): +def get_regional_address_details(party_details, doctype, company): pass -def set_contact_details(out, party, party_type): - out.contact_person = get_default_contact(party_type, party.name) +def set_contact_details(party_details, party, party_type): + party_details.contact_person = get_default_contact(party_type, party.name) - if not out.contact_person: - out.update({ + if not party_details.contact_person: + party_details.update({ "contact_person": None, "contact_display": None, "contact_email": None, @@ -125,22 +130,22 @@ def set_contact_details(out, party, party_type): "contact_department": None }) else: - out.update(get_contact_details(out.contact_person)) + party_details.update(get_contact_details(party_details.contact_person)) -def set_other_values(out, party, party_type): +def set_other_values(party_details, party, party_type): # copy if party_type=="Customer": to_copy = ["customer_name", "customer_group", "territory", "language"] else: to_copy = ["supplier_name", "supplier_group", "language"] for f in to_copy: - out[f] = party.get(f) + party_details[f] = party.get(f) # fields prepended with default in Customer doctype for f in ['currency'] \ + (['sales_partner', 'commission_rate'] if party_type=="Customer" else []): if party.get("default_" + f): - out[f] = party.get("default_" + f) + party_details[f] = party.get("default_" + f) def get_default_price_list(party): """Return default price list for party (Document object)""" @@ -155,7 +160,7 @@ def get_default_price_list(party): return None -def set_price_list(out, party, party_type, given_price_list, pos=None): +def set_price_list(party_details, party, party_type, given_price_list, pos=None): # price list price_list = get_permitted_documents('Price List') @@ -173,9 +178,9 @@ def set_price_list(out, party, party_type, given_price_list, pos=None): price_list = get_default_price_list(party) or given_price_list if price_list: - out.price_list_currency = frappe.db.get_value("Price List", price_list, "currency", cache=True) + party_details.price_list_currency = frappe.db.get_value("Price List", price_list, "currency", cache=True) - out["selling_price_list" if party.doctype=="Customer" else "buying_price_list"] = price_list + party_details["selling_price_list" if party.doctype=="Customer" else "buying_price_list"] = price_list def set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype): diff --git a/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.html b/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.html index 69e42c3bdbc..6fe69990513 100644 --- a/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.html +++ b/erpnext/accounts/print_format/bank_and_cash_payment_voucher/bank_and_cash_payment_voucher.html @@ -49,7 +49,7 @@ {% endfor %} Total (debit) - {{ gl | sum(attribute='debit') }} + {{ frappe.format((gl | sum(attribute="debit")), {fieldtype: "Currency"}) }} Credit @@ -69,7 +69,7 @@ {% endfor %} Total (credit) - {{ gl | sum(attribute='credit') }} + {{ frappe.format((gl | sum(attribute="credit")), {fieldtype: "Currency"}) }}
diff --git a/erpnext/accounts/print_format/gst_pos_invoice/gst_pos_invoice.json b/erpnext/accounts/print_format/gst_pos_invoice/gst_pos_invoice.json index 8a313688b98..1c5a195132d 100644 --- a/erpnext/accounts/print_format/gst_pos_invoice/gst_pos_invoice.json +++ b/erpnext/accounts/print_format/gst_pos_invoice/gst_pos_invoice.json @@ -1,22 +1,23 @@ { - "align_labels_right": 0, - "creation": "2017-08-08 12:33:04.773099", - "custom_format": 1, - "disabled": 0, - "doc_type": "Sales Invoice", - "docstatus": 0, - "doctype": "Print Format", - "font": "Default", - "html": "\n\n

\n\t{{ doc.company }}
\n\t{% if doc.company_address_display %}\n\t\t{% set company_address = doc.company_address_display.replace(\"\\n\", \" \").replace(\"
\", \" \") %}\n\t\t{% if \"GSTIN\" not in company_address %}\n\t\t\t{{ company_address }}\n\t\t\t{{ _(\"GSTIN\") }}:{{ doc.company_gstin }}\n\t\t{% else %}\n\t\t\t{{ company_address.replace(\"GSTIN\", \"
GSTIN\") }}\n\t\t{% endif %}\n\t{% endif %}\n\t
\n\t{% if doc.docstatus == 0 %}\n\t\t{{ doc.status + \" \"+ (doc.select_print_heading or _(\"Invoice\")) }}
\n\t{% else %}\n\t\t{{ doc.select_print_heading or _(\"Invoice\") }}
\n\t{% endif %}\n

\n

\n\t{{ _(\"Receipt No\") }}: {{ doc.name }}
\n\t{{ _(\"Date\") }}: {{ doc.get_formatted(\"posting_date\") }}
\n\t{% if doc.grand_total > 50000 %}\n\t\t{% set customer_address = doc.address_display.replace(\"\\n\", \" \").replace(\"
\", \" \") %}\n\t\t{{ _(\"Customer\") }}:
\n\t\t{{ doc.customer_name }}
\n\t\t{{ customer_address }}\n\t{% endif %}\n

\n\n
\n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n\t\t{%- for item in doc.items -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endfor -%}\n\t\n
{{ _(\"Item\") }}{{ _(\"Qty\") }}{{ _(\"Amount\") }}
\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t
{{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.gst_hsn_code -%}\n\t\t\t\t\t
{{ _(\"HSN/SAC\") }}: {{ item.gst_hsn_code }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t
{{ _(\"Serial No\") }}: {{ item.serial_no }}\n\t\t\t\t{%- endif -%}\n\t\t\t
{{ item.qty }}
@ {{ item.rate }}
{{ item.get_formatted(\"amount\") }}
\n\n\t\n\t\t\n\t\t\t{% if doc.flags.show_inclusive_tax_in_print %}\n\t\t\t\t\n\t\t\t\t\n\t\t\t{% else %}\n\t\t\t\t\n\t\t\t\t\n\t\t\t{% endif %}\n\t\t\n\t\t{%- for row in doc.taxes -%}\n\t\t {%- if (not row.included_in_print_rate or doc.flags.show_inclusive_tax_in_print) and row.tax_amount != 0 -%}\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t {%- endif -%}\n\t\t{%- endfor -%}\n\t\t{%- if doc.discount_amount -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endif -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- if doc.rounded_total -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endif -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t{%- if doc.change_amount -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t{%- endif -%}\n\t\n
\n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t
\n\t\t\t\t\t{{ row.description }}\n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t
\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Grand Total\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Rounded Total\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Paid Amount\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Change Amount\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t
\n

{{ doc.terms or \"\" }}

\n

{{ _(\"Thank you, please visit again.\") }}

", - "idx": 0, - "line_breaks": 0, - "modified": "2019-01-24 17:09:27.190929", - "modified_by": "Administrator", - "module": "Accounts", - "name": "GST POS Invoice", - "owner": "Administrator", - "print_format_builder": 0, - "print_format_type": "Server", - "show_section_headings": 0, + "align_labels_right": 0, + "creation": "2017-08-08 12:33:04.773099", + "custom_format": 1, + "disabled": 0, + "doc_type": "Sales Invoice", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "html": "\n\n{% if letter_head %}\n {{ letter_head }}\n{% endif %}\n

\n\t{{ doc.company }}
\n\t{% if doc.company_address_display %}\n\t\t{% set company_address = doc.company_address_display.replace(\"\\n\", \" \").replace(\"
\", \" \") %}\n\t\t{% if \"GSTIN\" not in company_address %}\n\t\t\t{{ company_address }}\n\t\t\t{{ _(\"GSTIN\") }}:{{ doc.company_gstin }}\n\t\t{% else %}\n\t\t\t{{ company_address.replace(\"GSTIN\", \"
GSTIN\") }}\n\t\t{% endif %}\n\t{% endif %}\n\t
\n\t{% if doc.docstatus == 0 %}\n\t\t{{ doc.status + \" \"+ (doc.select_print_heading or _(\"Invoice\")) }}
\n\t{% else %}\n\t\t{{ doc.select_print_heading or _(\"Invoice\") }}
\n\t{% endif %}\n

\n

\n\t{{ _(\"Receipt No\") }}: {{ doc.name }}
\n\t{{ _(\"Date\") }}: {{ doc.get_formatted(\"posting_date\") }}
\n\t{% if doc.grand_total > 50000 %}\n\t\t{% set customer_address = doc.address_display.replace(\"\\n\", \" \").replace(\"
\", \" \") %}\n\t\t{{ _(\"Customer\") }}:
\n\t\t{{ doc.customer_name }}
\n\t\t{{ customer_address }}\n\t{% endif %}\n

\n\n
\n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n\t\t{%- for item in doc.items -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endfor -%}\n\t\n
{{ _(\"Item\") }}{{ _(\"Qty\") }}{{ _(\"Amount\") }}
\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t
{{ item.item_name }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.gst_hsn_code -%}\n\t\t\t\t\t
{{ _(\"HSN/SAC\") }}: {{ item.gst_hsn_code }}\n\t\t\t\t{%- endif -%}\n\t\t\t\t{%- if item.serial_no -%}\n\t\t\t\t\t
{{ _(\"Serial No\") }}: {{ item.serial_no }}\n\t\t\t\t{%- endif -%}\n\t\t\t
{{ item.qty }}
@ {{ item.rate }}
{{ item.get_formatted(\"amount\") }}
\n\n\t\n\t\t\n\t\t\t{% if doc.flags.show_inclusive_tax_in_print %}\n\t\t\t\t\n\t\t\t\t\n\t\t\t{% else %}\n\t\t\t\t\n\t\t\t\t\n\t\t\t{% endif %}\n\t\t\n\t\t{%- for row in doc.taxes -%}\n\t\t {%- if (not row.included_in_print_rate or doc.flags.show_inclusive_tax_in_print) and row.tax_amount != 0 -%}\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t {%- endif -%}\n\t\t{%- endfor -%}\n\t\t{%- if doc.discount_amount -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endif -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- if doc.rounded_total -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endif -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t{%- if doc.change_amount -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t{%- endif -%}\n\t\n
\n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t
\n\t\t\t\t\t{{ row.description }}\n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t
\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Grand Total\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Rounded Total\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Paid Amount\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Change Amount\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t
\n

{{ doc.terms or \"\" }}

\n

{{ _(\"Thank you, please visit again.\") }}

", + "idx": 0, + "line_breaks": 0, + "modified": "2019-12-09 17:39:23.356573", + "modified_by": "Administrator", + "module": "Accounts", + "name": "GST POS Invoice", + "owner": "Administrator", + "print_format_builder": 0, + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 0, "standard": "Yes" } \ No newline at end of file diff --git a/erpnext/accounts/print_format/pos_invoice/pos_invoice.json b/erpnext/accounts/print_format/pos_invoice/pos_invoice.json index c3450d6a73c..be699228c52 100644 --- a/erpnext/accounts/print_format/pos_invoice/pos_invoice.json +++ b/erpnext/accounts/print_format/pos_invoice/pos_invoice.json @@ -1,21 +1,22 @@ { - "align_labels_right": 0, - "creation": "2011-12-21 11:08:55", - "custom_format": 1, - "disabled": 0, - "doc_type": "Sales Invoice", - "docstatus": 0, - "doctype": "Print Format", - "html": "\n\n

\n\t{{ doc.company }}
\n\t{% if doc.docstatus == 0 %}\n\t\t{{ doc.status + \" \" + (doc.select_print_heading or _(\"Invoice\")) }}
\n\t{% else %}\n\t\t{{ doc.select_print_heading or _(\"Invoice\") }}
\n\t{% endif %}\n

\n

\n\t{{ _(\"Receipt No\") }}: {{ doc.name }}
\n\t{{ _(\"Date\") }}: {{ doc.get_formatted(\"posting_date\") }}
\n\t{{ _(\"Customer\") }}: {{ doc.customer_name }}\n

\n\n
\n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n\t\t{%- for item in doc.items -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endfor -%}\n\t\n
{{ _(\"Item\") }}{{ _(\"Qty\") }}{{ _(\"Amount\") }}
\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t
{{ item.item_name }}{%- endif -%}\n\t\t\t
{{ item.qty }}
@ {{ item.get_formatted(\"rate\") }}
{{ item.get_formatted(\"amount\") }}
\n\n\t\n\t\t\n\t\t\t{% if doc.flags.show_inclusive_tax_in_print %}\n\t\t\t\t\n\t\t\t\t\n\t\t\t{% else %}\n\t\t\t\t\n\t\t\t\t\n\t\t\t{% endif %}\n\t\t\n\t\t{%- for row in doc.taxes -%}\n\t\t {%- if not row.included_in_print_rate or doc.flags.show_inclusive_tax_in_print -%}\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t {%- endif -%}\n\t\t{%- endfor -%}\n\n\t\t{%- if doc.discount_amount -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endif -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- if doc.rounded_total -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endif -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- if doc.change_amount -%}\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t{%- endif -%}\n\t\t{%- if doc.pos_total_qty -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endif -%}\n\t\n
\n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t
\n\t\t\t\t\t{{ row.description }}\n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t
\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Grand Total\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Rounded Total\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Paid Amount\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t
\n\t\t\t\t\t{{ _(\"Change Amount\") }}\n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t\t
\n\t\t\t\t{{ _(\"Total Qty\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"pos_total_qty\") }}\n\t\t\t
\n
\n

{{ doc.terms or \"\" }}

\n

{{ _(\"Thank you, please visit again.\") }}

", - "idx": 1, - "line_breaks": 0, - "modified": "2018-03-20 14:24:12.394354", - "modified_by": "Administrator", - "module": "Accounts", - "name": "POS Invoice", - "owner": "Administrator", - "print_format_builder": 0, - "print_format_type": "Server", - "show_section_headings": 0, + "align_labels_right": 0, + "creation": "2011-12-21 11:08:55", + "custom_format": 1, + "disabled": 0, + "doc_type": "Sales Invoice", + "docstatus": 0, + "doctype": "Print Format", + "html": "\n\n{% if letter_head %}\n {{ letter_head }}\n{% endif %}\n\n

\n\t{{ doc.company }}
\n\t{{ doc.select_print_heading or _(\"Invoice\") }}
\n

\n

\n\t{{ _(\"Receipt No\") }}: {{ doc.name }}
\n\t{{ _(\"Date\") }}: {{ doc.get_formatted(\"posting_date\") }}
\n\t{{ _(\"Customer\") }}: {{ doc.customer_name }}\n

\n\n
\n\n\t\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\n\t\n\t\t{%- for item in doc.items -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endfor -%}\n\t\n
{{ _(\"Item\") }}{{ _(\"Qty\") }}{{ _(\"Amount\") }}
\n\t\t\t\t{{ item.item_code }}\n\t\t\t\t{%- if item.item_name != item.item_code -%}\n\t\t\t\t\t
{{ item.item_name }}{%- endif -%}\n\t\t\t
{{ item.qty }}
@ {{ item.get_formatted(\"rate\") }}
{{ item.get_formatted(\"amount\") }}
\n\n\t\n\t\t\n\t\t\t{% if doc.flags.show_inclusive_tax_in_print %}\n\t\t\t\t\n\t\t\t\t\n\t\t\t{% else %}\n\t\t\t\t\n\t\t\t\t\n\t\t\t{% endif %}\n\t\t\n\t\t{%- for row in doc.taxes -%}\n\t\t {%- if not row.included_in_print_rate or doc.flags.show_inclusive_tax_in_print -%}\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t {%- endif -%}\n\t\t{%- endfor -%}\n\n\t\t{%- if doc.discount_amount -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endif -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- if doc.rounded_total -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- endif -%}\n\t\t\n\t\t\t\n\t\t\t\n\t\t\n\t\t{%- if doc.change_amount -%}\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\n\t\t{%- endif -%}\n\t\n
\n\t\t\t\t\t{{ _(\"Total Excl. Tax\") }}\n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"net_total\", doc) }}\n\t\t\t\t\n\t\t\t\t\t{{ _(\"Total\") }}\n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"total\", doc) }}\n\t\t\t\t
\n\t\t\t\t\t{{ row.description }}\n\t\t\t\t\n\t\t\t\t\t{{ row.get_formatted(\"tax_amount\", doc) }}\n\t\t\t\t
\n\t\t\t\t{{ _(\"Discount\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"discount_amount\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Grand Total\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"grand_total\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Rounded Total\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"rounded_total\") }}\n\t\t\t
\n\t\t\t\t{{ _(\"Paid Amount\") }}\n\t\t\t\n\t\t\t\t{{ doc.get_formatted(\"paid_amount\") }}\n\t\t\t
\n\t\t\t\t\t{{ _(\"Change Amount\") }}\n\t\t\t\t\n\t\t\t\t\t{{ doc.get_formatted(\"change_amount\") }}\n\t\t\t\t
\n
\n

{{ doc.terms or \"\" }}

\n

{{ _(\"Thank you, please visit again.\") }}

", + "idx": 1, + "line_breaks": 0, + "modified": "2019-12-09 17:40:53.183574", + "modified_by": "Administrator", + "module": "Accounts", + "name": "POS Invoice", + "owner": "Administrator", + "print_format_builder": 0, + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 0, "standard": "Yes" } \ No newline at end of file diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 8eb670de510..b1f427ca7f6 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -100,6 +100,11 @@ frappe.query_reports["Accounts Payable"] = { "fieldtype": "Link", "options": "Supplier Group" }, + { + "fieldname":"based_on_payment_terms", + "label": __("Based On Payment Terms"), + "fieldtype": "Check", + }, { "fieldname":"tax_id", "label": __("Tax Id"), diff --git a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js index 5f0fdc9f2c7..4a9f1b0dc44 100644 --- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js +++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js @@ -88,6 +88,11 @@ frappe.query_reports["Accounts Payable Summary"] = { "label": __("Supplier Group"), "fieldtype": "Link", "options": "Supplier Group" + }, + { + "fieldname":"based_on_payment_terms", + "label": __("Based On Payment Terms"), + "fieldtype": "Check", } ], diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index bcbd427186c..f82146a1df8 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -60,6 +60,7 @@ class ReceivablePayableReport(object): def get_data(self): self.get_gl_entries() + self.get_sales_invoices_or_customers_based_on_sales_person() self.voucher_balance = OrderedDict() self.init_voucher_balance() # invoiced, paid, credit_note, outstanding @@ -103,12 +104,18 @@ class ReceivablePayableReport(object): def get_invoices(self, gle): if gle.voucher_type in ('Sales Invoice', 'Purchase Invoice'): - self.invoices.add(gle.voucher_no) + if self.filters.get("sales_person"): + if gle.voucher_no in self.sales_person_records.get("Sales Invoice", []) \ + or gle.party in self.sales_person_records.get("Customer", []): + self.invoices.add(gle.voucher_no) + else: + self.invoices.add(gle.voucher_no) def update_voucher_balance(self, gle): # get the row where this balance needs to be updated # if its a payment, it will return the linked invoice or will be considered as advance row = self.get_voucher_balance(gle) + if not row: return # gle_balance will be the total "debit - credit" for receivable type reports and # and vice-versa for payable type reports gle_balance = self.get_gle_balance(gle) @@ -129,8 +136,13 @@ class ReceivablePayableReport(object): row.paid -= gle_balance def get_voucher_balance(self, gle): - voucher_balance = None + if self.filters.get("sales_person"): + against_voucher = gle.against_voucher or gle.voucher_no + if not (gle.party in self.sales_person_records.get("Customer", []) or \ + against_voucher in self.sales_person_records.get("Sales Invoice", [])): + return + voucher_balance = None if gle.against_voucher: # find invoice against_voucher = gle.against_voucher @@ -159,7 +171,7 @@ class ReceivablePayableReport(object): row.outstanding = flt(row.invoiced - row.paid - row.credit_note, self.currency_precision) row.invoice_grand_total = row.invoiced - if abs(row.outstanding) > 0.1/10 ** self.currency_precision: + if abs(row.outstanding) > 1.0/10 ** self.currency_precision: # non-zero oustanding, we must consider this row if self.is_invoice(row) and self.filters.based_on_payment_terms: @@ -188,7 +200,11 @@ class ReceivablePayableReport(object): self.data.append(row) def set_invoice_details(self, row): - row.update(self.invoice_details.get(row.voucher_no, {})) + invoice_details = self.invoice_details.get(row.voucher_no, {}) + if row.due_date: + invoice_details.pop("due_date", None) + row.update(invoice_details) + if row.voucher_type == 'Sales Invoice': if self.filters.show_delivery_notes: self.set_delivery_notes(row) @@ -269,7 +285,7 @@ class ReceivablePayableReport(object): def set_party_details(self, row): # customer / supplier name - party_details = self.get_party_details(row.party) + party_details = self.get_party_details(row.party) or {} row.update(party_details) if self.filters.get(scrub(self.filters.party_type)): row.currency = row.account_currency @@ -314,7 +330,7 @@ class ReceivablePayableReport(object): self.append_payment_term(row, d, term) def append_payment_term(self, row, d, term): - if self.filters.get("customer") and d.currency == d.party_account_currency: + if (self.filters.get("customer") or self.filters.get("supplier")) and d.currency == d.party_account_currency: invoiced = d.payment_amount else: invoiced = flt(flt(d.payment_amount) * flt(d.conversion_rate), self.currency_precision) @@ -508,6 +524,22 @@ class ReceivablePayableReport(object): order by posting_date, party""" .format(select_fields, conditions), values, as_dict=True) + def get_sales_invoices_or_customers_based_on_sales_person(self): + if self.filters.get("sales_person"): + lft, rgt = frappe.db.get_value("Sales Person", + self.filters.get("sales_person"), ["lft", "rgt"]) + + records = frappe.db.sql(""" + select distinct parent, parenttype + from `tabSales Team` steam + where parenttype in ('Customer', 'Sales Invoice') + and exists(select name from `tabSales Person` where lft >= %s and rgt <= %s and name = steam.sales_person) + """, (lft, rgt), as_dict=1) + + self.sales_person_records = frappe._dict() + for d in records: + self.sales_person_records.setdefault(d.parenttype, set()).add(d.parent) + def prepare_conditions(self): conditions = [""] values = [self.party_type, self.filters.report_date] @@ -560,16 +592,6 @@ class ReceivablePayableReport(object): conditions.append("party in (select name from tabCustomer where default_sales_partner=%s)") values.append(self.filters.get("sales_partner")) - if self.filters.get("sales_person"): - lft, rgt = frappe.db.get_value("Sales Person", - self.filters.get("sales_person"), ["lft", "rgt"]) - - conditions.append("""exists(select name from `tabSales Team` steam where - steam.sales_person in (select name from `tabSales Person` where lft >= {0} and rgt <= {1}) - and ((steam.parent = voucher_no and steam.parenttype = voucher_type) - or (steam.parent = against_voucher and steam.parenttype = against_voucher_type) - or (steam.parent = party and steam.parenttype = 'Customer')))""".format(lft, rgt)) - def add_supplier_filters(self, conditions, values): if self.filters.get("supplier_group"): conditions.append("""party in (select name from tabSupplier diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js index 0120608a8ff..d54824b6855 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js @@ -106,6 +106,11 @@ frappe.query_reports["Accounts Receivable Summary"] = { "label": __("Sales Person"), "fieldtype": "Link", "options": "Sales Person" + }, + { + "fieldname":"based_on_payment_terms", + "label": __("Based On Payment Terms"), + "fieldtype": "Check", } ], diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py index b90a7a9501b..b607c0f7028 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py @@ -36,6 +36,9 @@ class AccountsReceivableSummary(ReceivablePayableReport): self.filters.report_date) or {} for party, party_dict in iteritems(self.party_total): + if party_dict.outstanding == 0: + continue + row = frappe._dict() row.party = party diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index 0c99f1424cf..78546609adb 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -4,126 +4,141 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import formatdate, getdate, flt, add_days +from frappe.utils import formatdate, flt, add_days + def execute(filters=None): filters.day_before_from_date = add_days(filters.from_date, -1) columns, data = get_columns(filters), get_data(filters) return columns, data - + + def get_data(filters): data = [] - + asset_categories = get_asset_categories(filters) assets = get_assets(filters) - asset_costs = get_asset_costs(assets, filters) - asset_depreciations = get_accumulated_depreciations(assets, filters) - + for asset_category in asset_categories: row = frappe._dict() - row.asset_category = asset_category - row.update(asset_costs.get(asset_category)) + # row.asset_category = asset_category + row.update(asset_category) + + row.cost_as_on_to_date = (flt(row.cost_as_on_from_date) + flt(row.cost_of_new_purchase) - + flt(row.cost_of_sold_asset) - flt(row.cost_of_scrapped_asset)) + + row.update(next(asset for asset in assets if asset["asset_category"] == asset_category.get("asset_category", ""))) + row.accumulated_depreciation_as_on_to_date = (flt(row.accumulated_depreciation_as_on_from_date) + + flt(row.depreciation_amount_during_the_period) - flt(row.depreciation_eliminated)) + + row.net_asset_value_as_on_from_date = (flt(row.cost_as_on_from_date) - + flt(row.accumulated_depreciation_as_on_from_date)) + + row.net_asset_value_as_on_to_date = (flt(row.cost_as_on_to_date) - + flt(row.accumulated_depreciation_as_on_to_date)) - row.cost_as_on_to_date = (flt(row.cost_as_on_from_date) + flt(row.cost_of_new_purchase) - - flt(row.cost_of_sold_asset) - flt(row.cost_of_scrapped_asset)) - - row.update(asset_depreciations.get(asset_category)) - row.accumulated_depreciation_as_on_to_date = (flt(row.accumulated_depreciation_as_on_from_date) + - flt(row.depreciation_amount_during_the_period) - flt(row.depreciation_eliminated)) - - row.net_asset_value_as_on_from_date = (flt(row.cost_as_on_from_date) - - flt(row.accumulated_depreciation_as_on_from_date)) - - row.net_asset_value_as_on_to_date = (flt(row.cost_as_on_to_date) - - flt(row.accumulated_depreciation_as_on_to_date)) - data.append(row) - + return data - + + def get_asset_categories(filters): - return frappe.db.sql_list(""" - select distinct asset_category from `tabAsset` - where docstatus=1 and company=%s and purchase_date <= %s - """, (filters.company, filters.to_date)) - + return frappe.db.sql(""" + SELECT asset_category, + ifnull(sum(case when purchase_date < %(from_date)s then + case when ifnull(disposal_date, 0) = 0 or disposal_date >= %(from_date)s then + gross_purchase_amount + else + 0 + end + else + 0 + end), 0) as cost_as_on_from_date, + ifnull(sum(case when purchase_date >= %(from_date)s then + gross_purchase_amount + else + 0 + end), 0) as cost_of_new_purchase, + ifnull(sum(case when ifnull(disposal_date, 0) != 0 + and disposal_date >= %(from_date)s + and disposal_date <= %(to_date)s then + case when status = "Sold" then + gross_purchase_amount + else + 0 + end + else + 0 + end), 0) as cost_of_sold_asset, + ifnull(sum(case when ifnull(disposal_date, 0) != 0 + and disposal_date >= %(from_date)s + and disposal_date <= %(to_date)s then + case when status = "Scrapped" then + gross_purchase_amount + else + 0 + end + else + 0 + end), 0) as cost_of_scrapped_asset + from `tabAsset` + where docstatus=1 and company=%(company)s and purchase_date <= %(to_date)s + group by asset_category + """, {"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company}, as_dict=1) + + def get_assets(filters): return frappe.db.sql(""" - select name, asset_category, purchase_date, gross_purchase_amount, disposal_date, status - from `tabAsset` - where docstatus=1 and company=%s and purchase_date <= %s""", - (filters.company, filters.to_date), as_dict=1) - -def get_asset_costs(assets, filters): - asset_costs = frappe._dict() - for d in assets: - asset_costs.setdefault(d.asset_category, frappe._dict({ - "cost_as_on_from_date": 0, - "cost_of_new_purchase": 0, - "cost_of_sold_asset": 0, - "cost_of_scrapped_asset": 0 - })) - - costs = asset_costs[d.asset_category] - - if getdate(d.purchase_date) < getdate(filters.from_date): - if not d.disposal_date or getdate(d.disposal_date) >= getdate(filters.from_date): - costs.cost_as_on_from_date += flt(d.gross_purchase_amount) - else: - costs.cost_of_new_purchase += flt(d.gross_purchase_amount) - - if d.disposal_date and getdate(d.disposal_date) >= getdate(filters.from_date) \ - and getdate(d.disposal_date) <= getdate(filters.to_date): - if d.status == "Sold": - costs.cost_of_sold_asset += flt(d.gross_purchase_amount) - elif d.status == "Scrapped": - costs.cost_of_scrapped_asset += flt(d.gross_purchase_amount) - - return asset_costs - -def get_accumulated_depreciations(assets, filters): - asset_depreciations = frappe._dict() - for d in assets: - asset = frappe.get_doc("Asset", d.name) - - if d.asset_category in asset_depreciations: - asset_depreciations[d.asset_category]['accumulated_depreciation_as_on_from_date'] += asset.opening_accumulated_depreciation - else: - asset_depreciations.setdefault(d.asset_category, frappe._dict({ - "accumulated_depreciation_as_on_from_date": asset.opening_accumulated_depreciation, - "depreciation_amount_during_the_period": 0, - "depreciation_eliminated_during_the_period": 0 - })) + SELECT results.asset_category, + sum(results.accumulated_depreciation_as_on_from_date) as accumulated_depreciation_as_on_from_date, + sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period, + sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period + from (SELECT a.asset_category, + ifnull(sum(a.opening_accumulated_depreciation + + case when ds.schedule_date < %(from_date)s and + (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then + ds.depreciation_amount + else + 0 + end), 0) as accumulated_depreciation_as_on_from_date, + ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s + and a.disposal_date <= %(to_date)s and ds.schedule_date <= a.disposal_date then + ds.depreciation_amount + else + 0 + end), 0) as depreciation_eliminated_during_the_period, - depr = asset_depreciations[d.asset_category] + ifnull(sum(case when ds.schedule_date >= %(from_date)s and ds.schedule_date <= %(to_date)s + and (ifnull(a.disposal_date, 0) = 0 or ds.schedule_date <= a.disposal_date) then + ds.depreciation_amount + else + 0 + end), 0) as depreciation_amount_during_the_period + from `tabAsset` a, `tabDepreciation Schedule` ds + where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent + group by a.asset_category + union + SELECT a.asset_category, + ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 + and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) then + 0 + else + a.opening_accumulated_depreciation + end), 0) as accumulated_depreciation_as_on_from_date, + ifnull(sum(case when a.disposal_date >= %(from_date)s and a.disposal_date <= %(to_date)s then + a.opening_accumulated_depreciation + else + 0 + end), 0) as depreciation_eliminated_during_the_period, + 0 as depreciation_amount_during_the_period + from `tabAsset` a + where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s + and not exists(select * from `tabDepreciation Schedule` ds where a.name = ds.parent) + group by a.asset_category) as results + group by results.asset_category + """, {"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company}, as_dict=1) - if not asset.schedules: # if no schedule, - if asset.disposal_date: - # and disposal is NOT within the period, then opening accumulated depreciation not included - if getdate(asset.disposal_date) < getdate(filters.from_date) or getdate(asset.disposal_date) > getdate(filters.to_date): - asset_depreciations[d.asset_category]['accumulated_depreciation_as_on_from_date'] = 0 - # if no schedule, and disposal is within period, accumulated dep is the amount eliminated - if getdate(asset.disposal_date) >= getdate(filters.from_date) and getdate(asset.disposal_date) <= getdate(filters.to_date): - depr.depreciation_eliminated_during_the_period += asset.opening_accumulated_depreciation - - for schedule in asset.get("schedules"): - if getdate(schedule.schedule_date) < getdate(filters.from_date): - if not asset.disposal_date or getdate(asset.disposal_date) >= getdate(filters.from_date): - depr.accumulated_depreciation_as_on_from_date += flt(schedule.depreciation_amount) - elif getdate(schedule.schedule_date) <= getdate(filters.to_date): - if not asset.disposal_date: - depr.depreciation_amount_during_the_period += flt(schedule.depreciation_amount) - else: - if getdate(schedule.schedule_date) <= getdate(asset.disposal_date): - depr.depreciation_amount_during_the_period += flt(schedule.depreciation_amount) - - if asset.disposal_date and getdate(asset.disposal_date) >= getdate(filters.from_date) and getdate(asset.disposal_date) <= getdate(filters.to_date): - if getdate(schedule.schedule_date) <= getdate(asset.disposal_date): - depr.depreciation_eliminated_during_the_period += flt(schedule.depreciation_amount) - - return asset_depreciations - def get_columns(filters): return [ { diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js index 4bc29da2c7d..c4c24c0bab3 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.js +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js @@ -2,7 +2,7 @@ // License: GNU General Public License v3. See license.txt frappe.require("assets/erpnext/js/financial_statements.js", function() { - frappe.query_reports["Balance Sheet"] = erpnext.financial_statements; + frappe.query_reports["Balance Sheet"] = $.extend({}, erpnext.financial_statements); frappe.query_reports["Balance Sheet"]["filters"].push({ "fieldname": "accumulated_values", @@ -14,6 +14,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Balance Sheet"]["filters"].push({ "fieldname": "include_default_book_entries", "label": __("Include Default Book Entries"), - "fieldtype": "Check" + "fieldtype": "Check", + "default": 1 }); }); diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js index 24511871fdb..3ec4d306c35 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js @@ -46,13 +46,24 @@ frappe.query_reports["Budget Variance Report"] = { fieldtype: "Select", options: ["Cost Center", "Project"], default: "Cost Center", - reqd: 1 + reqd: 1, + on_change: function() { + frappe.query_report.set_filter_value("budget_against_filter", []); + frappe.query_report.refresh(); + } }, { - fieldname: "cost_center", - label: __("Cost Center"), - fieldtype: "Link", - options: "Cost Center" + fieldname:"budget_against_filter", + label: __('Dimension Filter'), + fieldtype: "MultiSelectList", + get_data: function(txt) { + if (!frappe.query_report.filters) return; + + let budget_against = frappe.query_report.get_filter_value('budget_against'); + if (!budget_against) return; + + return frappe.db.get_link_options(budget_against, txt); + } }, { fieldname:"show_cumulative", diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py index 8d65ac87148..39e218bfad2 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py @@ -12,22 +12,22 @@ from six import iteritems from pprint import pprint def execute(filters=None): if not filters: filters = {} - validate_filters(filters) + columns = get_columns(filters) - if filters.get("cost_center"): - cost_centers = [filters.get("cost_center")] + if filters.get("budget_against_filter"): + dimensions = filters.get("budget_against_filter") else: - cost_centers = get_cost_centers(filters) + dimensions = get_cost_centers(filters) period_month_ranges = get_period_month_ranges(filters["period"], filters["from_fiscal_year"]) - cam_map = get_cost_center_account_month_map(filters) + cam_map = get_dimension_account_month_map(filters) data = [] - for cost_center in cost_centers: - cost_center_items = cam_map.get(cost_center) - if cost_center_items: - for account, monthwise_data in iteritems(cost_center_items): - row = [cost_center, account] + for dimension in dimensions: + dimension_items = cam_map.get(dimension) + if dimension_items: + for account, monthwise_data in iteritems(dimension_items): + row = [dimension, account] totals = [0, 0, 0] for year in get_fiscal_years(filters): last_total = 0 @@ -55,10 +55,6 @@ def execute(filters=None): return columns, data -def validate_filters(filters): - if filters.get("budget_against") != "Cost Center" and filters.get("cost_center"): - frappe.throw(_("Filter based on Cost Center is only applicable if Budget Against is selected as Cost Center")) - def get_columns(filters): columns = [_(filters.get("budget_against")) + ":Link/%s:150"%(filters.get("budget_against")), _("Account") + ":Link/Account:150"] @@ -98,11 +94,12 @@ def get_cost_centers(filters): else: return frappe.db.sql_list("""select name from `tab{tab}`""".format(tab=filters.get("budget_against"))) #nosec -#Get cost center & target details -def get_cost_center_target_details(filters): +#Get dimension & target details +def get_dimension_target_details(filters): cond = "" - if filters.get("cost_center"): - cond += " and b.cost_center=%s" % frappe.db.escape(filters.get("cost_center")) + if filters.get("budget_against_filter"): + cond += " and b.{budget_against} in (%s)".format(budget_against = \ + frappe.scrub(filters.get('budget_against'))) % ', '.join(['%s']* len(filters.get('budget_against_filter'))) return frappe.db.sql(""" select b.{budget_against} as budget_against, b.monthly_distribution, ba.account, ba.budget_amount,b.fiscal_year @@ -110,8 +107,8 @@ def get_cost_center_target_details(filters): where b.name=ba.parent and b.docstatus = 1 and b.fiscal_year between %s and %s and b.budget_against = %s and b.company=%s {cond} order by b.fiscal_year """.format(budget_against=filters.get("budget_against").replace(" ", "_").lower(), cond=cond), - (filters.from_fiscal_year,filters.to_fiscal_year,filters.budget_against, filters.company), as_dict=True) - + tuple([filters.from_fiscal_year,filters.to_fiscal_year,filters.budget_against, filters.company] + filters.get('budget_against_filter')), + as_dict=True) #Get target distribution details of accounts of cost center @@ -153,14 +150,14 @@ def get_actual_details(name, filters): return cc_actual_details -def get_cost_center_account_month_map(filters): +def get_dimension_account_month_map(filters): import datetime - cost_center_target_details = get_cost_center_target_details(filters) + dimension_target_details = get_dimension_target_details(filters) tdd = get_target_distribution_details(filters) cam_map = {} - for ccd in cost_center_target_details: + for ccd in dimension_target_details: actual_details = get_actual_details(ccd.budget_against, filters) for month_id in range(1, 13): diff --git a/erpnext/accounts/report/cash_flow/cash_flow.js b/erpnext/accounts/report/cash_flow/cash_flow.js index 03940f4b246..89244c3781a 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.js +++ b/erpnext/accounts/report/cash_flow/cash_flow.js @@ -20,7 +20,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { { "fieldname": "include_default_book_entries", "label": __("Include Default Book Entries"), - "fieldtype": "Check" + "fieldtype": "Check", + "default": 1 } ); }); \ No newline at end of file diff --git a/erpnext/accounts/report/cash_flow/cash_flow.py b/erpnext/accounts/report/cash_flow/cash_flow.py index 98c25b7514a..e349a6aaaf0 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.py +++ b/erpnext/accounts/report/cash_flow/cash_flow.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import cint +from frappe.utils import cint, cstr from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data) from erpnext.accounts.report.profit_and_loss_statement.profit_and_loss_statement import get_net_profit_loss from erpnext.accounts.utils import get_fiscal_year @@ -129,13 +129,13 @@ def get_account_type_based_gl_data(company, start_date, end_date, account_type, cond = "" filters = frappe._dict(filters) - if filters.finance_book: - cond = " and finance_book = %s" %(frappe.db.escape(filters.finance_book)) - if filters.include_default_book_entries: - company_fb = frappe.db.get_value("Company", company, 'default_finance_book') + if filters.include_default_book_entries: + company_fb = frappe.db.get_value("Company", company, 'default_finance_book') + cond = """ AND (finance_book in (%s, %s, '') OR finance_book IS NULL) + """ %(frappe.db.escape(filters.finance_book), frappe.db.escape(company_fb)) + else: + cond = " AND (finance_book in (%s, '') OR finance_book IS NULL)" %(frappe.db.escape(cstr(filters.finance_book))) - cond = """ and finance_book in (%s, %s) - """ %(frappe.db.escape(filters.finance_book), frappe.db.escape(company_fb)) gl_sum = frappe.db.sql_list(""" select sum(credit) - sum(debit) diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js index e69a993e8ce..48a030a99ed 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js @@ -58,7 +58,8 @@ frappe.query_reports["Consolidated Financial Statement"] = { { "fieldname": "include_default_book_entries", "label": __("Include Default Book Entries"), - "fieldtype": "Check" + "fieldtype": "Check", + "default": 1 } ] } diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index 418a23c2d7e..4a79b6a340e 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -387,11 +387,10 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters): if from_date: additional_conditions.append("gl.posting_date >= %(from_date)s") - if filters.get("finance_book"): - if filters.get("include_default_book_entries"): - additional_conditions.append("finance_book in (%(finance_book)s, %(company_fb)s)") - else: - additional_conditions.append("finance_book in (%(finance_book)s)") + if filters.get("include_default_book_entries"): + additional_conditions.append("(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)") + else: + additional_conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)") return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else "" diff --git a/erpnext/accounts/report/financial_statements.html b/erpnext/accounts/report/financial_statements.html index 4081723bf0f..50947ecf5ef 100644 --- a/erpnext/accounts/report/financial_statements.html +++ b/erpnext/accounts/report/financial_statements.html @@ -1,5 +1,6 @@ {% var report_columns = report.get_columns_for_print(); + report_columns = report_columns.filter(col => !col.hidden); if (report_columns.length > 8) { frappe.throw(__("Too many columns. Export the report and print it using a spreadsheet application.")); @@ -15,34 +16,35 @@ height: 37px; } -{% var letterhead= filters.letter_head || (frappe.get_doc(":Company", filters.company) && frappe.get_doc(":Company", filters.company).default_letter_head) %} -{% if(letterhead) { %} -
- {%= frappe.boot.letter_heads[letterhead].header %} -
-{% } %} +

{%= __(report.report_name) %}

{%= filters.company %}

+ {% if 'cost_center' in filters %}

{%= filters.cost_center %}

{% endif %} +

{%= filters.fiscal_year %}

-
{%= __("Currency") %} : {%= filters.presentation_currency || erpnext.get_currency(filters.company) %}
+
+ {%= __("Currency") %} : {%= filters.presentation_currency || erpnext.get_currency(filters.company) %} +
{% if (filters.from_date) { %} -

{%= frappe.datetime.str_to_user(filters.from_date) %} - {%= frappe.datetime.str_to_user(filters.to_date) %}

+
+ {%= frappe.datetime.str_to_user(filters.from_date) %} - {%= frappe.datetime.str_to_user(filters.to_date) %} +
{% } %}
- - {% for(var i=2, l=report_columns.length; i + {% for (let i=1, l=report_columns.length; i{%= report_columns[i].label %} {% } %} - {% for(var j=0, k=data.length-1; j {%= row.account_name %} - {% for(var i=2, l=report_columns.length; i - {% var fieldname = report_columns[i].fieldname; %} + {% const fieldname = report_columns[i].fieldname; %} {% if (!is_null(row[fieldname])) { %} - {%= format_currency(row[fieldname], filters.presentation_currency) %} + {%= frappe.format(row[fieldname], report_columns[i], {}, row) %} {% } %} {% } %} @@ -64,4 +66,6 @@ {% } %}
-

Printed On {%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}

+

+ Printed On {%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %} +

diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 3c8de6026a6..35915d0fc64 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -13,7 +13,7 @@ import frappe, erpnext from erpnext.accounts.report.utils import get_currency, convert_to_presentation_currency from erpnext.accounts.utils import get_fiscal_year from frappe import _ -from frappe.utils import (flt, getdate, get_first_day, add_months, add_days, formatdate) +from frappe.utils import (flt, getdate, get_first_day, add_months, add_days, formatdate, cstr) from six import itervalues from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions @@ -175,7 +175,7 @@ def calculate_values( d = accounts_by_name.get(entry.account) if not d: frappe.msgprint( - _("Could not retrieve information for {0}.".format(entry.account)), title="Error", + _("Could not retrieve information for {0}.").format(entry.account), title="Error", raise_exception=1 ) for period in period_list: @@ -264,8 +264,8 @@ def filter_out_zero_value_rows(data, parent_children_map, show_zero_values=False def add_total_row(out, root_type, balance_must_be, period_list, company_currency): total_row = { - "account_name": "'" + _("Total {0} ({1})").format(_(root_type), _(balance_must_be)) + "'", - "account": "'" + _("Total {0} ({1})").format(_(root_type), _(balance_must_be)) + "'", + "account_name": _("Total {0} ({1})").format(_(root_type), _(balance_must_be)), + "account": _("Total {0} ({1})").format(_(root_type), _(balance_must_be)), "currency": company_currency } @@ -348,40 +348,42 @@ def set_gl_entries_by_account( additional_conditions = get_additional_conditions(from_date, ignore_closing_entries, filters) accounts = frappe.db.sql_list("""select name from `tabAccount` - where lft >= %s and rgt <= %s""", (root_lft, root_rgt)) - additional_conditions += " and account in ({})"\ - .format(", ".join([frappe.db.escape(d) for d in accounts])) + where lft >= %s and rgt <= %s and company = %s""", (root_lft, root_rgt, company)) - gl_filters = { - "company": company, - "from_date": from_date, - "to_date": to_date, - "finance_book": filters.get("finance_book") - } + if accounts: + additional_conditions += " and account in ({})"\ + .format(", ".join([frappe.db.escape(d) for d in accounts])) - if filters.get("include_default_book_entries"): - gl_filters["company_fb"] = frappe.db.get_value("Company", - company, 'default_finance_book') + gl_filters = { + "company": company, + "from_date": from_date, + "to_date": to_date, + "finance_book": cstr(filters.get("finance_book")) + } - for key, value in filters.items(): - if value: - gl_filters.update({ - key: value - }) + if filters.get("include_default_book_entries"): + gl_filters["company_fb"] = frappe.db.get_value("Company", + company, 'default_finance_book') - gl_entries = frappe.db.sql("""select posting_date, account, debit, credit, is_opening, fiscal_year, debit_in_account_currency, credit_in_account_currency, account_currency from `tabGL Entry` - where company=%(company)s - {additional_conditions} - and posting_date <= %(to_date)s - order by account, posting_date""".format(additional_conditions=additional_conditions), gl_filters, as_dict=True) #nosec + for key, value in filters.items(): + if value: + gl_filters.update({ + key: value + }) - if filters and filters.get('presentation_currency'): - convert_to_presentation_currency(gl_entries, get_currency(filters)) + gl_entries = frappe.db.sql("""select posting_date, account, debit, credit, is_opening, fiscal_year, debit_in_account_currency, credit_in_account_currency, account_currency from `tabGL Entry` + where company=%(company)s + {additional_conditions} + and posting_date <= %(to_date)s + order by account, posting_date""".format(additional_conditions=additional_conditions), gl_filters, as_dict=True) #nosec - for entry in gl_entries: - gl_entries_by_account.setdefault(entry.account, []).append(entry) + if filters and filters.get('presentation_currency'): + convert_to_presentation_currency(gl_entries, get_currency(filters)) - return gl_entries_by_account + for entry in gl_entries: + gl_entries_by_account.setdefault(entry.account, []).append(entry) + + return gl_entries_by_account def get_additional_conditions(from_date, ignore_closing_entries, filters): @@ -406,12 +408,11 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters): filters.cost_center = get_cost_centers_with_children(filters.cost_center) additional_conditions.append("cost_center in %(cost_center)s") - if filters.get("finance_book"): - if filters.get("include_default_book_entries"): - additional_conditions.append("finance_book in (%(finance_book)s, %(company_fb)s)") - else: - additional_conditions.append("finance_book in (%(finance_book)s)") - + if filters.get("include_default_book_entries"): + additional_conditions.append("(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)") + else: + additional_conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)") + if accounting_dimensions: for dimension in accounting_dimensions: if filters.get(dimension): @@ -430,7 +431,7 @@ def get_cost_centers_with_children(cost_centers): children = frappe.get_all("Cost Center", filters={"lft": [">=", lft], "rgt": ["<=", rgt]}) all_cost_centers += [c.name for c in children] else: - frappe.throw(_("Cost Center: {0} does not exist".format(d))) + frappe.throw(_("Cost Center: {0} does not exist").format(d)) return list(set(all_cost_centers)) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.html b/erpnext/accounts/report/general_ledger/general_ledger.html index 17da3b915f8..40469aecc1d 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.html +++ b/erpnext/accounts/report/general_ledger/general_ledger.html @@ -33,7 +33,7 @@ - {% for(var i=0, l=data.length-1; i {% if(data[i].posting_date) { %} {%= frappe.datetime.str_to_user(data[i].posting_date) %} diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index 4a287060b39..ac49d373d47 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -154,7 +154,8 @@ frappe.query_reports["General Ledger"] = { { "fieldname": "include_default_book_entries", "label": __("Include Default Book Entries"), - "fieldtype": "Check" + "fieldtype": "Check", + "default": 1 } ] } diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index ec3fb1fc9c2..8bea36571a6 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -119,7 +119,7 @@ def get_gl_entries(filters): select_fields = """, debit, credit, debit_in_account_currency, credit_in_account_currency """ - order_by_statement = "order by posting_date, account" + order_by_statement = "order by posting_date, account, creation" if filters.get("group_by") == _("Group by Voucher"): order_by_statement = "order by posting_date, voucher_type, voucher_no" @@ -184,7 +184,7 @@ def get_conditions(filters): if filters.get("finance_book"): if filters.get("include_default_book_entries"): - conditions.append("finance_book in (%(finance_book)s, %(company_fb)s)") + conditions.append("(finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)") else: conditions.append("finance_book in (%(finance_book)s)") @@ -373,19 +373,19 @@ def get_columns(filters): "width": 180 }, { - "label": _("Debit ({0})".format(currency)), + "label": _("Debit ({0})").format(currency), "fieldname": "debit", "fieldtype": "Float", "width": 100 }, { - "label": _("Credit ({0})".format(currency)), + "label": _("Credit ({0})").format(currency), "fieldname": "credit", "fieldtype": "Float", "width": 100 }, { - "label": _("Balance ({0})".format(currency)), + "label": _("Balance ({0})").format(currency), "fieldname": "balance", "fieldtype": "Float", "width": 130 diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js index a8362bf62dd..baa0bda7005 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -23,7 +23,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { { "fieldname": "include_default_book_entries", "label": __("Include Default Book Entries"), - "fieldtype": "Check" + "fieldtype": "Check", + "default": 1 } ); }); diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py index 0e2821ac16d..afdd31df16d 100644 --- a/erpnext/accounts/report/sales_register/sales_register.py +++ b/erpnext/accounts/report/sales_register/sales_register.py @@ -38,32 +38,46 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No cost_center = list(set(invoice_cc_wh_map.get(inv.name, {}).get("cost_center", []))) warehouse = list(set(invoice_cc_wh_map.get(inv.name, {}).get("warehouse", []))) - row = [ - inv.name, inv.posting_date, inv.customer, inv.customer_name - ] + row = { + 'invoice': inv.name, + 'posting_date': inv.posting_date, + 'customer': inv.customer, + 'customer_name': inv.customer_name + } if additional_query_columns: for col in additional_query_columns: - row.append(inv.get(col)) + row.update({ + col: inv.get(col) + }) + + row.update({ + 'customer_group': inv.get("customer_group"), + 'territory': inv.get("territory"), + 'tax_id': inv.get("tax_id"), + 'receivable_account': inv.debit_to, + 'mode_of_payment': ", ".join(mode_of_payments.get(inv.name, [])), + 'project': inv.project, + 'owner': inv.owner, + 'remarks': inv.remarks, + 'sales_order': ", ".join(sales_order), + 'delivery_note': ", ".join(delivery_note), + 'cost_center': ", ".join(cost_center), + 'warehouse': ", ".join(warehouse), + 'currency': company_currency + }) - row +=[ - inv.get("customer_group"), - inv.get("territory"), - inv.get("tax_id"), - inv.debit_to, ", ".join(mode_of_payments.get(inv.name, [])), - inv.project, inv.owner, inv.remarks, - ", ".join(sales_order), ", ".join(delivery_note),", ".join(cost_center), - ", ".join(warehouse), company_currency - ] # map income values base_net_total = 0 for income_acc in income_accounts: income_amount = flt(invoice_income_map.get(inv.name, {}).get(income_acc)) base_net_total += income_amount - row.append(income_amount) + row.update({ + frappe.scrub(income_acc): income_amount + }) # net total - row.append(base_net_total or inv.base_net_total) + row.update({'net_total': base_net_total or inv.base_net_total}) # tax account total_tax = 0 @@ -72,10 +86,18 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No tax_amount_precision = get_field_precision(frappe.get_meta("Sales Taxes and Charges").get_field("tax_amount"), currency=company_currency) or 2 tax_amount = flt(invoice_tax_map.get(inv.name, {}).get(tax_acc), tax_amount_precision) total_tax += tax_amount - row.append(tax_amount) + row.update({ + frappe.scrub(tax_acc): tax_amount + }) # total tax, grand total, outstanding amount & rounded total - row += [total_tax, inv.base_grand_total, inv.base_rounded_total, inv.outstanding_amount] + + row.update({ + 'tax_total': total_tax, + 'grand_total': inv.base_grand_total, + 'rounded_total': inv.base_rounded_total, + 'outstanding_amount': inv.outstanding_amount + }) data.append(row) @@ -84,19 +106,118 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No def get_columns(invoice_list, additional_table_columns): """return columns based on filters""" columns = [ - _("Invoice") + ":Link/Sales Invoice:120", _("Posting Date") + ":Date:80", - _("Customer") + ":Link/Customer:120", _("Customer Name") + "::120" + { + 'label': _("Invoice"), + 'fieldname': 'invoice', + 'fieldtype': 'Link', + 'options': 'Sales Invoice', + 'width': 120 + }, + { + 'label': _("Posting Date"), + 'fieldname': 'posting_date', + 'fieldtype': 'Date', + 'width': 80 + }, + { + 'label': _("Customer"), + 'fieldname': 'customer', + 'fieldtype': 'Link', + 'options': 'Customer', + 'width': 120 + }, + { + 'label': _("Customer Name"), + 'fieldname': 'customer_name', + 'fieldtype': 'Data', + 'width': 120 + }, ] if additional_table_columns: columns += additional_table_columns columns +=[ - _("Customer Group") + ":Link/Customer Group:120", _("Territory") + ":Link/Territory:80", - _("Tax Id") + "::80", _("Receivable Account") + ":Link/Account:120", _("Mode of Payment") + "::120", - _("Project") +":Link/Project:80", _("Owner") + "::150", _("Remarks") + "::150", - _("Sales Order") + ":Link/Sales Order:100", _("Delivery Note") + ":Link/Delivery Note:100", - _("Cost Center") + ":Link/Cost Center:100", _("Warehouse") + ":Link/Warehouse:100", + { + 'label': _("Custmer Group"), + 'fieldname': 'customer_group', + 'fieldtype': 'Link', + 'options': 'Customer Group', + 'width': 120 + }, + { + 'label': _("Territory"), + 'fieldname': 'territory', + 'fieldtype': 'Link', + 'options': 'Territory', + 'width': 80 + }, + { + 'label': _("Tax Id"), + 'fieldname': 'tax_id', + 'fieldtype': 'Data', + 'width': 120 + }, + { + 'label': _("Receivable Account"), + 'fieldname': 'receivable_account', + 'fieldtype': 'Link', + 'options': 'Account', + 'width': 80 + }, + { + 'label': _("Mode Of Payment"), + 'fieldname': 'mode_of_payment', + 'fieldtype': 'Data', + 'width': 120 + }, + { + 'label': _("Project"), + 'fieldname': 'project', + 'fieldtype': 'Link', + 'options': 'project', + 'width': 80 + }, + { + 'label': _("Owner"), + 'fieldname': 'owner', + 'fieldtype': 'Data', + 'width': 150 + }, + { + 'label': _("Remarks"), + 'fieldname': 'remarks', + 'fieldtype': 'Data', + 'width': 150 + }, + { + 'label': _("Sales Order"), + 'fieldname': 'sales_order', + 'fieldtype': 'Link', + 'options': 'Sales Order', + 'width': 100 + }, + { + 'label': _("Delivery Note"), + 'fieldname': 'delivery_note', + 'fieldtype': 'Link', + 'options': 'Delivery Note', + 'width': 100 + }, + { + 'label': _("Cost Center"), + 'fieldname': 'cost_center', + 'fieldtype': 'Link', + 'options': 'Cost Center', + 'width': 100 + }, + { + 'label': _("Warehouse"), + 'fieldname': 'warehouse', + 'fieldtype': 'Link', + 'options': 'Warehouse', + 'width': 100 + }, { "fieldname": "currency", "label": _("Currency"), @@ -105,7 +226,10 @@ def get_columns(invoice_list, additional_table_columns): } ] - income_accounts = tax_accounts = income_columns = tax_columns = [] + income_accounts = [] + tax_accounts = [] + income_columns = [] + tax_columns = [] if invoice_list: income_accounts = frappe.db.sql_list("""select distinct income_account @@ -119,14 +243,65 @@ def get_columns(invoice_list, additional_table_columns): and parent in (%s) order by account_head""" % ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) - income_columns = [(account + ":Currency/currency:120") for account in income_accounts] + for account in income_accounts: + income_columns.append({ + "label": account, + "fieldname": frappe.scrub(account), + "fieldtype": "Currency", + "options": 'currency', + "width": 120 + }) + for account in tax_accounts: if account not in income_accounts: - tax_columns.append(account + ":Currency/currency:120") + tax_columns.append({ + "label": account, + "fieldname": frappe.scrub(account), + "fieldtype": "Currency", + "options": 'currency', + "width": 120 + }) - columns = columns + income_columns + [_("Net Total") + ":Currency/currency:120"] + tax_columns + \ - [_("Total Tax") + ":Currency/currency:120", _("Grand Total") + ":Currency/currency:120", - _("Rounded Total") + ":Currency/currency:120", _("Outstanding Amount") + ":Currency/currency:120"] + net_total_column = [{ + "label": _("Net Total"), + "fieldname": "net_total", + "fieldtype": "Currency", + "options": 'currency', + "width": 120 + }] + + total_columns = [ + { + "label": _("Tax Total"), + "fieldname": "tax_total", + "fieldtype": "Currency", + "options": 'currency', + "width": 120 + }, + { + "label": _("Grand Total"), + "fieldname": "grand_total", + "fieldtype": "Currency", + "options": 'currency', + "width": 120 + }, + { + "label": _("Rounded Total"), + "fieldname": "rounded_total", + "fieldtype": "Currency", + "options": 'currency', + "width": 120 + }, + { + "label": _("Outstanding Amount"), + "fieldname": "outstanding_amount", + "fieldtype": "Currency", + "options": 'currency', + "width": 120 + } + ] + + columns = columns + income_columns + net_total_column + tax_columns + total_columns return columns, income_accounts, tax_accounts diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js index f15b5b1a19e..622bab6946f 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.js +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -85,7 +85,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { { "fieldname": "include_default_book_entries", "label": __("Include Default Book Entries"), - "fieldtype": "Check" + "fieldtype": "Check", + "default": 1 } ], "formatter": erpnext.financial_statements.formatter, diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index faeee0f76aa..69285cc9634 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -103,9 +103,9 @@ def get_rootwise_opening_balances(filters, report_type): where lft >= %s and rgt <= %s)""" % (lft, rgt) if filters.finance_book: - fb_conditions = " and finance_book = %(finance_book)s" + fb_conditions = " AND finance_book = %(finance_book)s" if filters.include_default_book_entries: - fb_conditions = " and (finance_book in (%(finance_book)s, %(company_fb)s))" + fb_conditions = " AND (finance_book in (%(finance_book)s, %(company_fb)s, '') OR finance_book IS NULL)" additional_conditions += fb_conditions diff --git a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js index 873c65ebf11..0e93035a35d 100644 --- a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js +++ b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js @@ -65,6 +65,21 @@ frappe.query_reports["Trial Balance for Party"] = { return party_type; } }, + { + "fieldname": "account", + "label": __("Account"), + "fieldtype": "Link", + "options": "Account", + "get_query": function() { + var company = frappe.query_report.get_filter_value('company'); + return { + "doctype": "Account", + "filters": { + "company": company, + } + } + } + }, { "fieldname": "show_zero_values", "label": __("Show zero values"), diff --git a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py index bd2c34b3b4c..78c7e439d38 100644 --- a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py +++ b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py @@ -18,14 +18,17 @@ def execute(filters=None): return columns, data def get_data(filters, show_party_name): - party_name_field = "{0}_name".format(frappe.scrub(filters.get('party_type'))) - if filters.get('party_type') == 'Student': + if filters.get('party_type') in ('Customer', 'Supplier', 'Employee', 'Member'): + party_name_field = "{0}_name".format(frappe.scrub(filters.get('party_type'))) + elif filters.get('party_type') == 'Student': party_name_field = 'first_name' elif filters.get('party_type') == 'Shareholder': party_name_field = 'title' + else: + party_name_field = 'name' party_filters = {"name": filters.get("party")} if filters.get("party") else {} - parties = frappe.get_all(filters.get("party_type"), fields = ["name", party_name_field], + parties = frappe.get_all(filters.get("party_type"), fields = ["name", party_name_field], filters = party_filters, order_by="name") company_currency = frappe.get_cached_value('Company', filters.company, "default_currency") opening_balances = get_opening_balances(filters) @@ -70,7 +73,7 @@ def get_data(filters, show_party_name): # totals for col in total_row: total_row[col] += row.get(col) - + row.update({ "currency": company_currency }) @@ -78,7 +81,7 @@ def get_data(filters, show_party_name): has_value = False if (opening_debit or opening_credit or debit or credit or closing_debit or closing_credit): has_value =True - + if cint(filters.show_zero_values) or has_value: data.append(row) @@ -93,13 +96,19 @@ def get_data(filters, show_party_name): return data def get_opening_balances(filters): + + account_filter = '' + if filters.get('account'): + account_filter = "and account = %s" % (frappe.db.escape(filters.get('account'))) + gle = frappe.db.sql(""" - select party, sum(debit) as opening_debit, sum(credit) as opening_credit + select party, sum(debit) as opening_debit, sum(credit) as opening_credit from `tabGL Entry` - where company=%(company)s + where company=%(company)s and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != '' and (posting_date < %(from_date)s or ifnull(is_opening, 'No') = 'Yes') - group by party""", { + {account_filter} + group by party""".format(account_filter=account_filter), { "company": filters.company, "from_date": filters.from_date, "party_type": filters.party_type @@ -113,14 +122,20 @@ def get_opening_balances(filters): return opening def get_balances_within_period(filters): + + account_filter = '' + if filters.get('account'): + account_filter = "and account = %s" % (frappe.db.escape(filters.get('account'))) + gle = frappe.db.sql(""" - select party, sum(debit) as debit, sum(credit) as credit + select party, sum(debit) as debit, sum(credit) as credit from `tabGL Entry` - where company=%(company)s + where company=%(company)s and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != '' - and posting_date >= %(from_date)s and posting_date <= %(to_date)s + and posting_date >= %(from_date)s and posting_date <= %(to_date)s and ifnull(is_opening, 'No') = 'No' - group by party""", { + {account_filter} + group by party""".format(account_filter=account_filter), { "company": filters.company, "from_date": filters.from_date, "to_date": filters.to_date, diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 382a89b310d..4789063ba50 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -513,7 +513,7 @@ def remove_ref_doc_link_from_jv(ref_type, ref_no): where reference_type=%s and reference_name=%s and docstatus < 2""", (now(), frappe.session.user, ref_type, ref_no)) - frappe.msgprint(_("Journal Entries {0} are un-linked".format("\n".join(linked_jv)))) + frappe.msgprint(_("Journal Entries {0} are un-linked").format("\n".join(linked_jv))) def remove_ref_doc_link_from_pe(ref_type, ref_no): linked_pe = frappe.db.sql_list("""select parent from `tabPayment Entry Reference` @@ -536,7 +536,7 @@ def remove_ref_doc_link_from_pe(ref_type, ref_no): where name=%s""", (pe_doc.total_allocated_amount, pe_doc.base_total_allocated_amount, pe_doc.unallocated_amount, now(), frappe.session.user, pe)) - frappe.msgprint(_("Payment Entries {0} are un-linked".format("\n".join(linked_pe)))) + frappe.msgprint(_("Payment Entries {0} are un-linked").format("\n".join(linked_pe))) @frappe.whitelist() def get_company_default(company, fieldname): @@ -569,7 +569,7 @@ def get_stock_and_account_balance(account=None, posting_date=None, company=None) warehouse_account = get_warehouse_account_map(company) - account_balance = get_balance_on(account, posting_date, in_account_currency=False) + account_balance = get_balance_on(account, posting_date, in_account_currency=False, ignore_account_permission=True) related_warehouses = [wh for wh, wh_details in warehouse_account.items() if wh_details.account == account and not wh_details.is_group] @@ -630,7 +630,7 @@ def get_held_invoices(party_type, party): 'select name from `tabPurchase Invoice` where release_date IS NOT NULL and release_date > CURDATE()', as_dict=1 ) - held_invoices = [d['name'] for d in held_invoices] + held_invoices = set([d['name'] for d in held_invoices]) return held_invoices @@ -639,14 +639,20 @@ def get_outstanding_invoices(party_type, party, account, condition=None, filters outstanding_invoices = [] precision = frappe.get_precision("Sales Invoice", "outstanding_amount") or 2 - if erpnext.get_party_account_type(party_type) == 'Receivable': + if account: + root_type, account_type = frappe.get_cached_value("Account", account, ["root_type", "account_type"]) + party_account_type = "Receivable" if root_type == "Asset" else "Payable" + party_account_type = account_type or party_account_type + else: + party_account_type = erpnext.get_party_account_type(party_type) + + if party_account_type == 'Receivable': dr_or_cr = "debit_in_account_currency - credit_in_account_currency" payment_dr_or_cr = "credit_in_account_currency - debit_in_account_currency" else: dr_or_cr = "credit_in_account_currency - debit_in_account_currency" payment_dr_or_cr = "debit_in_account_currency - credit_in_account_currency" - invoice = 'Sales Invoice' if erpnext.get_party_account_type(party_type) == 'Receivable' else 'Purchase Invoice' held_invoices = get_held_invoices(party_type, party) invoice_list = frappe.db.sql(""" @@ -665,7 +671,6 @@ def get_outstanding_invoices(party_type, party, account, condition=None, filters group by voucher_type, voucher_no order by posting_date, name""".format( dr_or_cr=dr_or_cr, - invoice = invoice, condition=condition or "" ), { "party_type": party_type, @@ -887,3 +892,9 @@ def get_allow_cost_center_in_entry_of_bs_account(): def generator(): return cint(frappe.db.get_value('Accounts Settings', None, 'allow_cost_center_in_entry_of_bs_account')) return frappe.local_cache("get_allow_cost_center_in_entry_of_bs_account", (), generator, regenerate_if_none=True) + +def get_stock_accounts(company): + return frappe.get_all("Account", filters = { + "account_type": "Stock", + "company": company + }) \ No newline at end of file diff --git a/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py b/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py index bb9045ca81e..cae150c428a 100644 --- a/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py +++ b/erpnext/agriculture/doctype/crop_cycle/crop_cycle.py @@ -44,34 +44,32 @@ class CropCycle(Document): self.import_disease_tasks(disease.disease, disease.start_date) disease.tasks_created = True - frappe.msgprint(_("Tasks have been created for managing the {0} disease (on row {1})".format(disease.disease, disease.idx))) + frappe.msgprint(_("Tasks have been created for managing the {0} disease (on row {1})").format(disease.disease, disease.idx)) def import_disease_tasks(self, disease, start_date): disease_doc = frappe.get_doc('Disease', disease) self.create_task(disease_doc.treatment_task, self.name, start_date) def create_project(self, period, crop_tasks): - project = frappe.new_doc("Project") - project.update({ + project = frappe.get_doc({ + "doctype": "Project", "project_name": self.title, "expected_start_date": self.start_date, "expected_end_date": add_days(self.start_date, period - 1) - }) - project.insert() + }).insert() return project.name def create_task(self, crop_tasks, project_name, start_date): for crop_task in crop_tasks: - task = frappe.new_doc("Task") - task.update({ + frappe.get_doc({ + "doctype": "Task", "subject": crop_task.get("task_name"), "priority": crop_task.get("priority"), "project": project_name, "exp_start_date": add_days(start_date, crop_task.get("start_day") - 1), "exp_end_date": add_days(start_date, crop_task.get("end_day") - 1) - }) - task.insert() + }).insert() def reload_linked_analysis(self): linked_doctypes = ['Soil Texture', 'Soil Analysis', 'Plant Analysis'] diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index c5cad738018..a53ff881777 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -41,6 +41,39 @@ frappe.ui.form.on('Asset', { }); }, + setup: function(frm) { + frm.make_methods = { + 'Asset Movement': () => { + frappe.call({ + method: "erpnext.assets.doctype.asset.asset.make_asset_movement", + freeze: true, + args:{ + "assets": [{ name: cur_frm.doc.name }] + }, + callback: function (r) { + if (r.message) { + var doc = frappe.model.sync(r.message)[0]; + frappe.set_route("Form", doc.doctype, doc.name); + } + } + }); + }, + } + + frm.set_query("purchase_receipt", (doc) => { + return { + query: "erpnext.controllers.queries.get_purchase_receipts", + filters: { item_code: doc.item_code } + } + }); + frm.set_query("purchase_invoice", (doc) => { + return { + query: "erpnext.controllers.queries.get_purchase_invoices", + filters: { item_code: doc.item_code } + } + }); + }, + refresh: function(frm) { frappe.ui.form.trigger("Asset", "is_existing_asset"); frm.toggle_display("next_depreciation_date", frm.doc.docstatus < 1); @@ -78,11 +111,6 @@ frappe.ui.form.on('Asset', { }); } - if (frm.doc.status=='Submitted' && !frm.doc.is_existing_asset && !frm.doc.purchase_invoice) { - frm.add_custom_button(__("Purchase Invoice"), function() { - frm.trigger("make_purchase_invoice"); - }, __('Create')); - } if (frm.doc.maintenance_required && !frm.doc.maintenance_schedule) { frm.add_custom_button(__("Asset Maintenance"), function() { frm.trigger("create_asset_maintenance"); @@ -104,11 +132,40 @@ frappe.ui.form.on('Asset', { frm.trigger("setup_chart"); } + frm.trigger("toggle_reference_doc"); + if (frm.doc.docstatus == 0) { frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation); } }, + toggle_reference_doc: function(frm) { + if (frm.doc.purchase_receipt && frm.doc.purchase_invoice && frm.doc.docstatus === 1) { + frm.set_df_property('purchase_invoice', 'read_only', 1); + frm.set_df_property('purchase_receipt', 'read_only', 1); + } + else if (frm.doc.is_existing_asset) { + frm.toggle_reqd('purchase_receipt', 0); + frm.toggle_reqd('purchase_invoice', 0); + } + else if (frm.doc.purchase_receipt) { + // if purchase receipt link is set then set PI disabled + frm.toggle_reqd('purchase_invoice', 0); + frm.set_df_property('purchase_invoice', 'read_only', 1); + } + else if (frm.doc.purchase_invoice) { + // if purchase invoice link is set then set PR disabled + frm.toggle_reqd('purchase_receipt', 0); + frm.set_df_property('purchase_receipt', 'read_only', 1); + } + else { + frm.toggle_reqd('purchase_receipt', 1); + frm.set_df_property('purchase_receipt', 'read_only', 0); + frm.toggle_reqd('purchase_invoice', 1); + frm.set_df_property('purchase_invoice', 'read_only', 0); + } + }, + make_journal_entry: function(frm) { frappe.call({ method: "erpnext.assets.doctype.asset.asset.make_journal_entry", @@ -176,21 +233,25 @@ frappe.ui.form.on('Asset', { item_code: function(frm) { if(frm.doc.item_code) { - frappe.call({ - method: "erpnext.assets.doctype.asset.asset.get_item_details", - args: { - item_code: frm.doc.item_code, - asset_category: frm.doc.asset_category - }, - callback: function(r, rt) { - if(r.message) { - frm.set_value('finance_books', r.message); - } - } - }) + frm.trigger('set_finance_book'); } }, + set_finance_book: function(frm) { + frappe.call({ + method: "erpnext.assets.doctype.asset.asset.get_item_details", + args: { + item_code: frm.doc.item_code, + asset_category: frm.doc.asset_category + }, + callback: function(r, rt) { + if(r.message) { + frm.set_value('finance_books', r.message); + } + } + }) + }, + available_for_use_date: function(frm) { $.each(frm.doc.finance_books || [], function(i, d) { if(!d.depreciation_start_date) d.depreciation_start_date = frm.doc.available_for_use_date; @@ -199,37 +260,23 @@ frappe.ui.form.on('Asset', { }, is_existing_asset: function(frm) { + frm.trigger("toggle_reference_doc"); // frm.toggle_reqd("next_depreciation_date", (!frm.doc.is_existing_asset && frm.doc.calculate_depreciation)); }, opening_accumulated_depreciation: function(frm) { - erpnext.asset.set_accululated_depreciation(frm); + erpnext.asset.set_accumulated_depreciation(frm); }, make_schedules_editable: function(frm) { - var is_editable = frm.doc.finance_books.filter(d => d.depreciation_method == "Manual").length > 0 - ? true : false; + if (frm.doc.finance_books) { + var is_editable = frm.doc.finance_books.filter(d => d.depreciation_method == "Manual").length > 0 + ? true : false; - frm.toggle_enable("schedules", is_editable); - frm.fields_dict["schedules"].grid.toggle_enable("schedule_date", is_editable); - frm.fields_dict["schedules"].grid.toggle_enable("depreciation_amount", is_editable); - }, - - make_purchase_invoice: function(frm) { - frappe.call({ - args: { - "asset": frm.doc.name, - "item_code": frm.doc.item_code, - "gross_purchase_amount": frm.doc.gross_purchase_amount, - "company": frm.doc.company, - "posting_date": frm.doc.purchase_date - }, - method: "erpnext.assets.doctype.asset.asset.make_purchase_invoice", - callback: function(r) { - var doclist = frappe.model.sync(r.message); - frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - }) + frm.toggle_enable("schedules", is_editable); + frm.fields_dict["schedules"].grid.toggle_enable("schedule_date", is_editable); + frm.fields_dict["schedules"].grid.toggle_enable("depreciation_amount", is_editable); + } }, make_sales_invoice: function(frm) { @@ -282,17 +329,6 @@ frappe.ui.form.on('Asset', { }, calculate_depreciation: function(frm) { - frappe.db.get_value("Asset Settings", {'name':"Asset Settings"}, 'schedule_based_on_fiscal_year', (data) => { - if (data.schedule_based_on_fiscal_year == 1) { - frm.set_df_property("depreciation_method", "options", "\nStraight Line\nManual"); - frm.toggle_reqd("available_for_use_date", true); - frm.toggle_display("frequency_of_depreciation", false); - frappe.db.get_value("Fiscal Year", {'name': frappe.sys_defaults.fiscal_year}, "year_end_date", (data) => { - frm.set_value("next_depreciation_date", data.year_end_date); - }) - } - }) - frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation); }, @@ -302,6 +338,58 @@ frappe.ui.form.on('Asset', { }) }, + purchase_receipt: (frm) => { + frm.trigger('toggle_reference_doc'); + if (frm.doc.purchase_receipt) { + if (frm.doc.item_code) { + frappe.db.get_doc('Purchase Receipt', frm.doc.purchase_receipt).then(pr_doc => { + frm.events.set_values_from_purchase_doc(frm, 'Purchase Receipt', pr_doc) + }); + } else { + frm.set_value('purchase_receipt', ''); + frappe.msgprint({ + title: __('Not Allowed'), + message: __("Please select Item Code first") + }); + } + } + }, + + purchase_invoice: (frm) => { + frm.trigger('toggle_reference_doc'); + if (frm.doc.purchase_invoice) { + if (frm.doc.item_code) { + frappe.db.get_doc('Purchase Invoice', frm.doc.purchase_invoice).then(pi_doc => { + frm.events.set_values_from_purchase_doc(frm, 'Purchase Invoice', pi_doc) + }); + } else { + frm.set_value('purchase_invoice', ''); + frappe.msgprint({ + title: __('Not Allowed'), + message: __("Please select Item Code first") + }); + } + } + }, + + set_values_from_purchase_doc: function(frm, doctype, purchase_doc) { + frm.set_value('company', purchase_doc.company); + frm.set_value('purchase_date', purchase_doc.posting_date); + const item = purchase_doc.items.find(item => item.item_code === frm.doc.item_code); + if (!item) { + doctype_field = frappe.scrub(doctype) + frm.set_value(doctype_field, ''); + frappe.msgprint({ + title: __(`Invalid ${doctype}`), + message: __(`The selected ${doctype} doesn't contains selected Asset Item.`), + indicator: 'red' + }); + } + frm.set_value('gross_purchase_amount', item.base_net_rate + item.item_tax_amount); + frm.set_value('purchase_receipt_amount', item.base_net_rate + item.item_tax_amount); + frm.set_value('location', item.asset_location); + }, + set_depreciation_rate: function(frm, row) { if (row.total_number_of_depreciations && row.frequency_of_depreciation && row.expected_value_after_useful_life) { @@ -371,12 +459,12 @@ frappe.ui.form.on('Depreciation Schedule', { }, depreciation_amount: function(frm, cdt, cdn) { - erpnext.asset.set_accululated_depreciation(frm); + erpnext.asset.set_accumulated_depreciation(frm); } }) -erpnext.asset.set_accululated_depreciation = function(frm) { +erpnext.asset.set_accumulated_depreciation = function(frm) { if(frm.doc.depreciation_method != "Manual") return; var accumulated_depreciation = flt(frm.doc.opening_accumulated_depreciation); @@ -415,92 +503,19 @@ erpnext.asset.restore_asset = function(frm) { }) }; -erpnext.asset.transfer_asset = function(frm) { - var dialog = new frappe.ui.Dialog({ - title: __("Transfer Asset"), - fields: [ - { - "label": __("Target Location"), - "fieldname": "target_location", - "fieldtype": "Link", - "options": "Location", - "get_query": function () { - return { - filters: [ - ["Location", "is_group", "=", 0] - ] - } - }, - "reqd": 1 - }, - { - "label": __("Select Serial No"), - "fieldname": "serial_nos", - "fieldtype": "Link", - "options": "Serial No", - "get_query": function () { - return { - filters: { - 'asset': frm.doc.name - } - } - }, - "onchange": function() { - let val = this.get_value(); - if (val) { - let serial_nos = dialog.get_value("serial_no") || val; - if (serial_nos) { - serial_nos = serial_nos.split('\n'); - serial_nos.push(val); - - const unique_sn = serial_nos.filter(function(elem, index, self) { - return index === self.indexOf(elem); - }); - - dialog.set_value("serial_no", unique_sn.join('\n')); - dialog.set_value("serial_nos", ""); - } - } - } - }, - { - "label": __("Serial No"), - "fieldname": "serial_no", - "read_only": 1, - "fieldtype": "Small Text" - }, - { - "label": __("Date"), - "fieldname": "transfer_date", - "fieldtype": "Datetime", - "reqd": 1, - "default": frappe.datetime.now_datetime() +erpnext.asset.transfer_asset = function() { + frappe.call({ + method: "erpnext.assets.doctype.asset.asset.make_asset_movement", + freeze: true, + args:{ + "assets": [{ name: cur_frm.doc.name }], + "purpose": "Transfer" + }, + callback: function (r) { + if (r.message) { + var doc = frappe.model.sync(r.message)[0]; + frappe.set_route("Form", doc.doctype, doc.name); } - ] + } }); - - dialog.set_primary_action(__("Transfer"), function() { - var args = dialog.get_values(); - if(!args) return; - dialog.hide(); - return frappe.call({ - type: "GET", - method: "erpnext.assets.doctype.asset.asset.transfer_asset", - args: { - args: { - "asset": frm.doc.name, - "transaction_date": args.transfer_date, - "source_location": frm.doc.location, - "target_location": args.target_location, - "serial_no": args.serial_no, - "company": frm.doc.company - } - }, - freeze: true, - callback: function(r) { - cur_frm.reload_doc(); - } - }) - }); - dialog.show(); }; diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index c60ec5ec3f9..97165a31d29 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -1,497 +1,505 @@ { - "allow_import": 1, - "allow_rename": 1, - "autoname": "naming_series:", - "creation": "2016-03-01 17:01:27.920130", - "doctype": "DocType", - "document_type": "Document", - "field_order": [ - "naming_series", - "asset_name", - "item_code", - "item_name", - "asset_category", - "asset_owner", - "asset_owner_company", - "supplier", - "customer", - "image", - "column_break_3", - "company", - "location", - "custodian", - "department", - "purchase_date", - "disposal_date", - "journal_entry_for_scrap", - "accounting_dimensions_section", - "cost_center", - "dimension_col_break", - "section_break_5", - "gross_purchase_amount", - "available_for_use_date", - "column_break_18", - "calculate_depreciation", - "is_existing_asset", - "opening_accumulated_depreciation", - "number_of_depreciations_booked", - "section_break_23", - "finance_books", - "section_break_33", - "depreciation_method", - "value_after_depreciation", - "total_number_of_depreciations", - "column_break_24", - "frequency_of_depreciation", - "next_depreciation_date", - "section_break_14", - "schedules", - "insurance_details", - "policy_number", - "insurer", - "insured_value", - "column_break_48", - "insurance_start_date", - "insurance_end_date", - "comprehensive_insurance", - "section_break_31", - "maintenance_required", - "other_details", - "status", - "booked_fixed_asset", - "column_break_51", - "purchase_receipt", - "purchase_receipt_amount", - "purchase_invoice", - "default_finance_book", - "amended_from" - ], - "fields": [ - { - "fieldname": "naming_series", - "fieldtype": "Select", - "label": "Naming Series", - "options": "ACC-ASS-.YYYY.-" - }, - { - "fieldname": "asset_name", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Asset Name", - "reqd": 1 - }, - { - "fieldname": "item_code", - "fieldtype": "Link", - "in_standard_filter": 1, - "label": "Item Code", - "options": "Item", - "reqd": 1 - }, - { - "fetch_from": "item_code.item_name", - "fieldname": "item_name", - "fieldtype": "Read Only", - "label": "Item Name" - }, - { - "fetch_from": "item_code.asset_category", - "fieldname": "asset_category", - "fieldtype": "Link", - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Asset Category", - "options": "Asset Category", - "read_only": 1 - }, - { - "fieldname": "asset_owner", - "fieldtype": "Select", - "label": "Asset Owner", - "options": "\nCompany\nSupplier\nCustomer" - }, - { - "depends_on": "eval:doc.asset_owner == \"Company\"", - "fieldname": "asset_owner_company", - "fieldtype": "Link", - "label": "Asset Owner Company", - "options": "Company" - }, - { - "depends_on": "eval:doc.asset_owner == \"Supplier\"", - "fieldname": "supplier", - "fieldtype": "Link", - "label": "Supplier", - "options": "Supplier" - }, - { - "depends_on": "eval:doc.asset_owner == \"Customer\"", - "fieldname": "customer", - "fieldtype": "Link", - "label": "Customer", - "options": "Customer" - }, - { - "allow_on_submit": 1, - "fieldname": "image", - "fieldtype": "Attach Image", - "hidden": 1, - "label": "Image", - "no_copy": 1, - "print_hide": 1 - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "remember_last_selected_value": 1, - "reqd": 1 - }, - { - "fieldname": "location", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Location", - "options": "Location", - "reqd": 1 - }, - { - "fieldname": "custodian", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Custodian", - "options": "Employee" - }, - { - "fieldname": "cost_center", - "fieldtype": "Link", - "label": "Cost Center", - "options": "Cost Center" - }, - { - "fieldname": "department", - "fieldtype": "Link", - "label": "Department", - "options": "Department" - }, - { - "fieldname": "purchase_date", - "fieldtype": "Date", - "label": "Purchase Date", - "reqd": 1 - }, - { - "fieldname": "disposal_date", - "fieldtype": "Date", - "label": "Disposal Date", - "read_only": 1 - }, - { - "fieldname": "journal_entry_for_scrap", - "fieldtype": "Link", - "label": "Journal Entry for Scrap", - "no_copy": 1, - "options": "Journal Entry", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "section_break_5", - "fieldtype": "Section Break" - }, - { - "fieldname": "gross_purchase_amount", - "fieldtype": "Currency", - "label": "Gross Purchase Amount", - "options": "Company:company:default_currency", - "reqd": 1 - }, - { - "fieldname": "available_for_use_date", - "fieldtype": "Date", - "label": "Available-for-use Date" - }, - { - "fieldname": "column_break_18", - "fieldtype": "Column Break" - }, - { - "default": "0", - "fieldname": "calculate_depreciation", - "fieldtype": "Check", - "label": "Calculate Depreciation" - }, - { - "default": "0", - "fieldname": "is_existing_asset", - "fieldtype": "Check", - "label": "Is Existing Asset" - }, - { - "depends_on": "is_existing_asset", - "fieldname": "opening_accumulated_depreciation", - "fieldtype": "Currency", - "label": "Opening Accumulated Depreciation", - "no_copy": 1, - "options": "Company:company:default_currency" - }, - { - "depends_on": "eval:(doc.is_existing_asset && doc.opening_accumulated_depreciation)", - "fieldname": "number_of_depreciations_booked", - "fieldtype": "Int", - "label": "Number of Depreciations Booked", - "no_copy": 1 - }, - { - "depends_on": "calculate_depreciation", - "fieldname": "section_break_23", - "fieldtype": "Section Break", - "label": "Depreciation" - }, - { - "fieldname": "finance_books", - "fieldtype": "Table", - "label": "Finance Books", - "options": "Asset Finance Book" - }, - { - "fieldname": "section_break_33", - "fieldtype": "Section Break", - "hidden": 1 - }, - { - "fieldname": "depreciation_method", - "fieldtype": "Select", - "label": "Depreciation Method", - "options": "\nStraight Line\nDouble Declining Balance\nManual" - }, - { - "fieldname": "value_after_depreciation", - "fieldtype": "Currency", - "hidden": 1, - "label": "Value After Depreciation", - "options": "Company:company:default_currency", - "read_only": 1 - }, - { - "fieldname": "total_number_of_depreciations", - "fieldtype": "Int", - "label": "Total Number of Depreciations" - }, - { - "fieldname": "column_break_24", - "fieldtype": "Column Break" - }, - { - "fieldname": "frequency_of_depreciation", - "fieldtype": "Int", - "label": "Frequency of Depreciation (Months)" - }, - { - "fieldname": "next_depreciation_date", - "fieldtype": "Date", - "label": "Next Depreciation Date", - "no_copy": 1 - }, - { - "depends_on": "calculate_depreciation", - "fieldname": "section_break_14", - "fieldtype": "Section Break", - "label": "Depreciation Schedule" - }, - { - "fieldname": "schedules", - "fieldtype": "Table", - "label": "Depreciation Schedules", - "no_copy": 1, - "options": "Depreciation Schedule" - }, - { - "collapsible": 1, - "fieldname": "insurance_details", - "fieldtype": "Section Break", - "label": "Insurance details" - }, - { - "fieldname": "policy_number", - "fieldtype": "Data", - "label": "Policy number" - }, - { - "fieldname": "insurer", - "fieldtype": "Data", - "label": "Insurer" - }, - { - "fieldname": "insured_value", - "fieldtype": "Data", - "label": "Insured value" - }, - { - "fieldname": "column_break_48", - "fieldtype": "Column Break" - }, - { - "fieldname": "insurance_start_date", - "fieldtype": "Date", - "label": "Insurance Start Date" - }, - { - "fieldname": "insurance_end_date", - "fieldtype": "Date", - "label": "Insurance End Date" - }, - { - "fieldname": "comprehensive_insurance", - "fieldtype": "Data", - "label": "Comprehensive Insurance" - }, - { - "fieldname": "section_break_31", - "fieldtype": "Section Break", - "label": "Maintenance" - }, - { - "allow_on_submit": 1, - "default": "0", - "description": "Check if Asset requires Preventive Maintenance or Calibration", - "fieldname": "maintenance_required", - "fieldtype": "Check", - "label": "Maintenance Required" - }, - { - "collapsible": 1, - "fieldname": "other_details", - "fieldtype": "Section Break", - "label": "Other Details" - }, - { - "allow_on_submit": 1, - "default": "Draft", - "fieldname": "status", - "fieldtype": "Select", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Status", - "no_copy": 1, - "options": "Draft\nSubmitted\nPartially Depreciated\nFully Depreciated\nSold\nScrapped\nIn Maintenance\nOut of Order\nIssue\nReceipt", - "read_only": 1 - }, - { - "default": "0", - "fieldname": "booked_fixed_asset", - "fieldtype": "Check", - "label": "Booked Fixed Asset", - "no_copy": 1, - "read_only": 1 - }, - { - "fieldname": "column_break_51", - "fieldtype": "Column Break" - }, - { - "fieldname": "purchase_receipt", - "fieldtype": "Link", - "label": "Purchase Receipt", - "no_copy": 1, - "options": "Purchase Receipt", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "purchase_receipt_amount", - "fieldtype": "Currency", - "hidden": 1, - "label": "Purchase Receipt Amount", - "no_copy": 1, - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "purchase_invoice", - "fieldtype": "Link", - "label": "Purchase Invoice", - "no_copy": 1, - "options": "Purchase Invoice", - "read_only": 1 - }, - { - "fetch_from": "company.default_finance_book", - "fieldname": "default_finance_book", - "fieldtype": "Link", - "hidden": 1, - "label": "Default Finance Book", - "options": "Finance Book", - "read_only": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "label": "Amended From", - "no_copy": 1, - "options": "Asset", - "print_hide": 1, - "read_only": 1 - }, - { - "collapsible": 1, - "fieldname": "accounting_dimensions_section", - "fieldtype": "Section Break", - "label": "Accounting Dimensions" - }, - { - "fieldname": "dimension_col_break", - "fieldtype": "Column Break" - } - ], - "idx": 72, - "image_field": "image", - "is_submittable": 1, - "modified": "2019-05-25 22:26:19.786201", - "modified_by": "Administrator", - "module": "Assets", - "name": "Asset", - "owner": "Administrator", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "import": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Quality Manager", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "asset_name" - } \ No newline at end of file + "allow_import": 1, + "allow_rename": 1, + "autoname": "naming_series:", + "creation": "2016-03-01 17:01:27.920130", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "naming_series", + "asset_name", + "item_code", + "item_name", + "asset_category", + "asset_owner", + "asset_owner_company", + "supplier", + "customer", + "image", + "purchase_invoice", + "column_break_3", + "company", + "location", + "custodian", + "department", + "purchase_date", + "disposal_date", + "journal_entry_for_scrap", + "purchase_receipt", + "accounting_dimensions_section", + "cost_center", + "dimension_col_break", + "section_break_5", + "gross_purchase_amount", + "available_for_use_date", + "column_break_18", + "calculate_depreciation", + "allow_monthly_depreciation", + "is_existing_asset", + "opening_accumulated_depreciation", + "number_of_depreciations_booked", + "section_break_23", + "finance_books", + "section_break_33", + "depreciation_method", + "value_after_depreciation", + "total_number_of_depreciations", + "column_break_24", + "frequency_of_depreciation", + "next_depreciation_date", + "section_break_14", + "schedules", + "insurance_details", + "policy_number", + "insurer", + "insured_value", + "column_break_48", + "insurance_start_date", + "insurance_end_date", + "comprehensive_insurance", + "section_break_31", + "maintenance_required", + "other_details", + "status", + "booked_fixed_asset", + "column_break_51", + + "purchase_receipt_amount", + "default_finance_book", + "amended_from" + ], + "fields": [ + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Naming Series", + "options": "ACC-ASS-.YYYY.-" + }, + { + "fieldname": "asset_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Asset Name", + "reqd": 1 + }, + { + "fieldname": "item_code", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Item Code", + "options": "Item", + "reqd": 1 + }, + { + "fetch_from": "item_code.item_name", + "fieldname": "item_name", + "fieldtype": "Read Only", + "label": "Item Name" + }, + { + "fetch_from": "item_code.asset_category", + "fieldname": "asset_category", + "fieldtype": "Link", + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Asset Category", + "options": "Asset Category", + "read_only": 1 + }, + { + "fieldname": "asset_owner", + "fieldtype": "Select", + "label": "Asset Owner", + "options": "\nCompany\nSupplier\nCustomer" + }, + { + "depends_on": "eval:doc.asset_owner == \"Company\"", + "fieldname": "asset_owner_company", + "fieldtype": "Link", + "label": "Asset Owner Company", + "options": "Company" + }, + { + "depends_on": "eval:doc.asset_owner == \"Supplier\"", + "fieldname": "supplier", + "fieldtype": "Link", + "label": "Supplier", + "options": "Supplier" + }, + { + "depends_on": "eval:doc.asset_owner == \"Customer\"", + "fieldname": "customer", + "fieldtype": "Link", + "label": "Customer", + "options": "Customer" + }, + { + "allow_on_submit": 1, + "fieldname": "image", + "fieldtype": "Attach Image", + "hidden": 1, + "label": "Image", + "no_copy": 1, + "print_hide": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "remember_last_selected_value": 1, + "reqd": 1 + }, + { + "fieldname": "location", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Location", + "options": "Location", + "reqd": 1 + }, + { + "fieldname": "custodian", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Custodian", + "options": "Employee" + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department" + }, + { + "fieldname": "purchase_date", + "fieldtype": "Date", + "label": "Purchase Date", + "reqd": 1 + }, + { + "fieldname": "disposal_date", + "fieldtype": "Date", + "label": "Disposal Date", + "read_only": 1 + }, + { + "fieldname": "journal_entry_for_scrap", + "fieldtype": "Link", + "label": "Journal Entry for Scrap", + "no_copy": 1, + "options": "Journal Entry", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "section_break_5", + "fieldtype": "Section Break" + }, + { + "fieldname": "gross_purchase_amount", + "fieldtype": "Currency", + "label": "Gross Purchase Amount", + "options": "Company:company:default_currency", + "reqd": 1 + }, + { + "fieldname": "available_for_use_date", + "fieldtype": "Date", + "label": "Available-for-use Date" + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "calculate_depreciation", + "fieldtype": "Check", + "label": "Calculate Depreciation" + }, + { + "default": "0", + "fieldname": "is_existing_asset", + "fieldtype": "Check", + "label": "Is Existing Asset" + }, + { + "depends_on": "is_existing_asset", + "fieldname": "opening_accumulated_depreciation", + "fieldtype": "Currency", + "label": "Opening Accumulated Depreciation", + "no_copy": 1, + "options": "Company:company:default_currency" + }, + { + "depends_on": "eval:(doc.is_existing_asset && doc.opening_accumulated_depreciation)", + "fieldname": "number_of_depreciations_booked", + "fieldtype": "Int", + "label": "Number of Depreciations Booked", + "no_copy": 1 + }, + { + "depends_on": "calculate_depreciation", + "fieldname": "section_break_23", + "fieldtype": "Section Break", + "label": "Depreciation" + }, + { + "fieldname": "finance_books", + "fieldtype": "Table", + "label": "Finance Books", + "options": "Asset Finance Book" + }, + { + "fieldname": "section_break_33", + "fieldtype": "Section Break", + "hidden": 1 + }, + { + "fieldname": "depreciation_method", + "fieldtype": "Select", + "label": "Depreciation Method", + "options": "\nStraight Line\nDouble Declining Balance\nManual" + }, + { + "fieldname": "value_after_depreciation", + "fieldtype": "Currency", + "hidden": 1, + "label": "Value After Depreciation", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "total_number_of_depreciations", + "fieldtype": "Int", + "label": "Total Number of Depreciations" + }, + { + "fieldname": "column_break_24", + "fieldtype": "Column Break" + }, + { + "fieldname": "frequency_of_depreciation", + "fieldtype": "Int", + "label": "Frequency of Depreciation (Months)" + }, + { + "fieldname": "next_depreciation_date", + "fieldtype": "Date", + "label": "Next Depreciation Date", + "no_copy": 1 + }, + { + "depends_on": "calculate_depreciation", + "fieldname": "section_break_14", + "fieldtype": "Section Break", + "label": "Depreciation Schedule" + }, + { + "fieldname": "schedules", + "fieldtype": "Table", + "label": "Depreciation Schedules", + "no_copy": 1, + "options": "Depreciation Schedule" + }, + { + "collapsible": 1, + "fieldname": "insurance_details", + "fieldtype": "Section Break", + "label": "Insurance details" + }, + { + "fieldname": "policy_number", + "fieldtype": "Data", + "label": "Policy number" + }, + { + "fieldname": "insurer", + "fieldtype": "Data", + "label": "Insurer" + }, + { + "fieldname": "insured_value", + "fieldtype": "Data", + "label": "Insured value" + }, + { + "fieldname": "column_break_48", + "fieldtype": "Column Break" + }, + { + "fieldname": "insurance_start_date", + "fieldtype": "Date", + "label": "Insurance Start Date" + }, + { + "fieldname": "insurance_end_date", + "fieldtype": "Date", + "label": "Insurance End Date" + }, + { + "fieldname": "comprehensive_insurance", + "fieldtype": "Data", + "label": "Comprehensive Insurance" + }, + { + "fieldname": "section_break_31", + "fieldtype": "Section Break", + "label": "Maintenance" + }, + { + "allow_on_submit": 1, + "default": "0", + "description": "Check if Asset requires Preventive Maintenance or Calibration", + "fieldname": "maintenance_required", + "fieldtype": "Check", + "label": "Maintenance Required" + }, + { + "collapsible": 1, + "fieldname": "other_details", + "fieldtype": "Section Break", + "label": "Other Details" + }, + { + "allow_on_submit": 1, + "default": "Draft", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Status", + "no_copy": 1, + "options": "Draft\nSubmitted\nPartially Depreciated\nFully Depreciated\nSold\nScrapped\nIn Maintenance\nOut of Order\nIssue\nReceipt", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "booked_fixed_asset", + "fieldtype": "Check", + "label": "Booked Fixed Asset", + "no_copy": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_51", + "fieldtype": "Column Break" + }, + { + "fieldname": "purchase_receipt", + "fieldtype": "Link", + "label": "Purchase Receipt", + "no_copy": 1, + "options": "Purchase Receipt", + "print_hide": 1 + }, + { + "fieldname": "purchase_receipt_amount", + "fieldtype": "Currency", + "hidden": 1, + "label": "Purchase Receipt Amount", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "purchase_invoice", + "fieldtype": "Link", + "label": "Purchase Invoice", + "no_copy": 1, + "options": "Purchase Invoice" + }, + { + "fetch_from": "company.default_finance_book", + "fieldname": "default_finance_book", + "fieldtype": "Link", + "hidden": 1, + "label": "Default Finance Book", + "options": "Finance Book", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Asset", + "print_hide": 1, + "read_only": 1 + }, + { + "collapsible": 1, + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" + }, + { + "default": "0", + "depends_on": "calculate_depreciation", + "fieldname": "allow_monthly_depreciation", + "fieldtype": "Check", + "label": "Allow Monthly Depreciation" + } + ], + "idx": 72, + "image_field": "image", + "is_submittable": 1, + "modified": "2019-10-22 15:47:36.050828", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Quality Manager", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "asset_name" +} \ No newline at end of file diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 6e2bbc16260..759d42a5427 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe, erpnext, math, json from frappe import _ from six import string_types -from frappe.utils import flt, add_months, cint, nowdate, getdate, today, date_diff, add_days +from frappe.utils import flt, add_months, cint, nowdate, getdate, today, date_diff, month_diff, add_days from frappe.model.document import Document from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account from erpnext.assets.doctype.asset.depreciation \ @@ -18,6 +18,7 @@ from erpnext.controllers.accounts_controller import AccountsController class Asset(AccountsController): def validate(self): self.validate_asset_values() + self.validate_asset_and_reference() self.validate_item() self.set_missing_values() self.prepare_depreciation_data() @@ -29,10 +30,13 @@ class Asset(AccountsController): def on_submit(self): self.validate_in_use_date() self.set_status() - self.update_stock_movement() - if not self.booked_fixed_asset and not is_cwip_accounting_disabled(): + self.make_asset_movement() + if not self.booked_fixed_asset and is_cwip_accounting_enabled(self.asset_category): self.make_gl_entries() + def before_cancel(self): + self.cancel_auto_gen_movement() + def on_cancel(self): self.validate_cancellation() self.delete_depreciation_entries() @@ -40,6 +44,18 @@ class Asset(AccountsController): delete_gl_entries(voucher_type='Asset', voucher_no=self.name) self.db_set('booked_fixed_asset', 0) + def validate_asset_and_reference(self): + if self.purchase_invoice or self.purchase_receipt: + reference_doc = 'Purchase Invoice' if self.purchase_invoice else 'Purchase Receipt' + reference_name = self.purchase_invoice or self.purchase_receipt + reference_doc = frappe.get_doc(reference_doc, reference_name) + if reference_doc.get('company') != self.company: + frappe.throw(_("Company of asset {0} and purchase document {1} doesn't matches.").format(self.name, reference_doc.get('name'))) + + + if self.is_existing_asset and self.purchase_invoice: + frappe.throw(_("Purchase Invoice cannot be made against an existing asset {0}").format(self.name)) + def prepare_depreciation_data(self): if self.calculate_depreciation: self.value_after_depreciation = 0 @@ -76,10 +92,13 @@ class Asset(AccountsController): self.set('finance_books', finance_books) def validate_asset_values(self): + if not self.asset_category: + self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category") + if not flt(self.gross_purchase_amount): frappe.throw(_("Gross Purchase Amount is mandatory"), frappe.MandatoryError) - if not is_cwip_accounting_disabled(): + if is_cwip_accounting_enabled(self.asset_category): if not self.is_existing_asset and not (self.purchase_receipt or self.purchase_invoice): frappe.throw(_("Please create purchase receipt or purchase invoice for the item {0}"). format(self.item_code)) @@ -105,6 +124,39 @@ class Asset(AccountsController): if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(self.purchase_date): frappe.throw(_("Available-for-use Date should be after purchase date")) + def cancel_auto_gen_movement(self): + movements = frappe.db.sql( + """SELECT asm.name, asm.docstatus + FROM `tabAsset Movement` asm, `tabAsset Movement Item` asm_item + WHERE asm_item.parent=asm.name and asm_item.asset=%s and asm.docstatus=1""", self.name, as_dict=1) + if len(movements) > 1: + frappe.throw(_('Asset has multiple Asset Movement Entries which has to be \ + cancelled manually to cancel this asset.')) + if movements: + movement = frappe.get_doc('Asset Movement', movements[0].get('name')) + movement.flags.ignore_validate = True + movement.cancel() + + def make_asset_movement(self): + reference_doctype = 'Purchase Receipt' if self.purchase_receipt else 'Purchase Invoice' + reference_docname = self.purchase_receipt or self.purchase_invoice + assets = [{ + 'asset': self.name, + 'asset_name': self.asset_name, + 'target_location': self.location, + 'to_employee': self.custodian + }] + asset_movement = frappe.get_doc({ + 'doctype': 'Asset Movement', + 'assets': assets, + 'purpose': 'Receipt', + 'company': self.company, + 'transaction_date': getdate(nowdate()), + 'reference_doctype': reference_doctype, + 'reference_name': reference_docname + }).insert() + asset_movement.submit() + def set_depreciation_rate(self): for d in self.get("finance_books"): d.rate_of_depreciation = flt(self.get_depreciation_rate(d, on_validate=True), @@ -145,19 +197,31 @@ class Asset(AccountsController): schedule_date = add_months(d.depreciation_start_date, n * cint(d.frequency_of_depreciation)) + # schedule date will be a year later from start date + # so monthly schedule date is calculated by removing 11 months from it + monthly_schedule_date = add_months(schedule_date, - d.frequency_of_depreciation + 1) + # For first row if has_pro_rata and n==0: - depreciation_amount, days = get_pro_rata_amt(d, depreciation_amount, + depreciation_amount, days, months = get_pro_rata_amt(d, depreciation_amount, self.available_for_use_date, d.depreciation_start_date) + + # For first depr schedule date will be the start date + # so monthly schedule date is calculated by removing month difference between use date and start date + monthly_schedule_date = add_months(d.depreciation_start_date, - months + 1) + # For last row elif has_pro_rata and n == cint(number_of_pending_depreciations) - 1: to_date = add_months(self.available_for_use_date, n * cint(d.frequency_of_depreciation)) - depreciation_amount, days = get_pro_rata_amt(d, + depreciation_amount, days, months = get_pro_rata_amt(d, depreciation_amount, schedule_date, to_date) + monthly_schedule_date = add_months(schedule_date, 1) + schedule_date = add_days(schedule_date, days) + last_schedule_date = schedule_date if not depreciation_amount: continue value_after_depreciation -= flt(depreciation_amount, @@ -171,13 +235,50 @@ class Asset(AccountsController): skip_row = True if depreciation_amount > 0: - self.append("schedules", { - "schedule_date": schedule_date, - "depreciation_amount": depreciation_amount, - "depreciation_method": d.depreciation_method, - "finance_book": d.finance_book, - "finance_book_id": d.idx - }) + # With monthly depreciation, each depreciation is divided by months remaining until next date + if self.allow_monthly_depreciation: + # month range is 1 to 12 + # In pro rata case, for first and last depreciation, month range would be different + month_range = months \ + if (has_pro_rata and n==0) or (has_pro_rata and n == cint(number_of_pending_depreciations) - 1) \ + else d.frequency_of_depreciation + + for r in range(month_range): + if (has_pro_rata and n == 0): + # For first entry of monthly depr + if r == 0: + days_until_first_depr = date_diff(monthly_schedule_date, self.available_for_use_date) + per_day_amt = depreciation_amount / days + depreciation_amount_for_current_month = per_day_amt * days_until_first_depr + depreciation_amount -= depreciation_amount_for_current_month + date = monthly_schedule_date + amount = depreciation_amount_for_current_month + else: + date = add_months(monthly_schedule_date, r) + amount = depreciation_amount / (month_range - 1) + elif (has_pro_rata and n == cint(number_of_pending_depreciations) - 1) and r == cint(month_range) - 1: + # For last entry of monthly depr + date = last_schedule_date + amount = depreciation_amount / month_range + else: + date = add_months(monthly_schedule_date, r) + amount = depreciation_amount / month_range + + self.append("schedules", { + "schedule_date": date, + "depreciation_amount": amount, + "depreciation_method": d.depreciation_method, + "finance_book": d.finance_book, + "finance_book_id": d.idx + }) + else: + self.append("schedules", { + "schedule_date": schedule_date, + "depreciation_amount": depreciation_amount, + "depreciation_method": d.depreciation_method, + "finance_book": d.finance_book, + "finance_book_id": d.idx + }) def check_is_pro_rata(self, row): has_pro_rata = False @@ -196,7 +297,9 @@ class Asset(AccountsController): .format(row.idx)) if not row.depreciation_start_date: - frappe.throw(_("Row {0}: Depreciation Start Date is required").format(row.idx)) + if not self.available_for_use_date: + frappe.throw(_("Row {0}: Depreciation Start Date is required").format(row.idx)) + row.depreciation_start_date = self.available_for_use_date if not self.is_existing_asset: self.opening_accumulated_depreciation = 0 @@ -345,22 +448,13 @@ class Asset(AccountsController): if d.finance_book == self.default_finance_book: return cint(d.idx) - 1 - def update_stock_movement(self): - asset_movement = frappe.db.get_value('Asset Movement', - {'asset': self.name, 'reference_name': self.purchase_receipt, 'docstatus': 0}, 'name') - - if asset_movement: - doc = frappe.get_doc('Asset Movement', asset_movement) - doc.naming_series = 'ACC-ASM-.YYYY.-' - doc.submit() - def make_gl_entries(self): gl_entries = [] - if ((self.purchase_receipt or (self.purchase_invoice and - frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock'))) + if ((self.purchase_receipt \ + or (self.purchase_invoice and frappe.db.get_value('Purchase Invoice', self.purchase_invoice, 'update_stock'))) and self.purchase_receipt_amount and self.available_for_use_date <= nowdate()): - fixed_aseet_account = get_asset_category_account(self.name, 'fixed_asset_account', + fixed_asset_account = get_asset_category_account('fixed_asset_account', asset=self.name, asset_category = self.asset_category, company = self.company) cwip_account = get_asset_account("capital_work_in_progress_account", @@ -368,7 +462,7 @@ class Asset(AccountsController): gl_entries.append(self.get_gl_dict({ "account": cwip_account, - "against": fixed_aseet_account, + "against": fixed_asset_account, "remarks": self.get("remarks") or _("Accounting Entry for Asset"), "posting_date": self.available_for_use_date, "credit": self.purchase_receipt_amount, @@ -377,7 +471,7 @@ class Asset(AccountsController): })) gl_entries.append(self.get_gl_dict({ - "account": fixed_aseet_account, + "account": fixed_asset_account, "against": cwip_account, "remarks": self.get("remarks") or _("Accounting Entry for Asset"), "posting_date": self.available_for_use_date, @@ -424,39 +518,23 @@ def update_maintenance_status(): asset.set_status('Out of Order') def make_post_gl_entry(): - if is_cwip_accounting_disabled(): - return - assets = frappe.db.sql_list(""" select name from `tabAsset` - where ifnull(booked_fixed_asset, 0) = 0 and available_for_use_date = %s""", nowdate()) + asset_categories = frappe.db.get_all('Asset Category', fields = ['name', 'enable_cwip_accounting']) - for asset in assets: - doc = frappe.get_doc('Asset', asset) - doc.make_gl_entries() + for asset_category in asset_categories: + if cint(asset_category.enable_cwip_accounting): + assets = frappe.db.sql_list(""" select name from `tabAsset` + where asset_category = %s and ifnull(booked_fixed_asset, 0) = 0 + and available_for_use_date = %s""", (asset_category.name, nowdate())) + + for asset in assets: + doc = frappe.get_doc('Asset', asset) + doc.make_gl_entries() def get_asset_naming_series(): meta = frappe.get_meta('Asset') return meta.get_field("naming_series").options -@frappe.whitelist() -def make_purchase_invoice(asset, item_code, gross_purchase_amount, company, posting_date): - pi = frappe.new_doc("Purchase Invoice") - pi.company = company - pi.currency = frappe.get_cached_value('Company', company, "default_currency") - pi.set_posting_time = 1 - pi.posting_date = posting_date - pi.append("items", { - "item_code": item_code, - "is_fixed_asset": 1, - "asset": asset, - "expense_account": get_asset_category_account(asset, 'fixed_asset_account'), - "qty": 1, - "price_list_rate": gross_purchase_amount, - "rate": gross_purchase_amount - }) - pi.set_missing_values() - return pi - @frappe.whitelist() def make_sales_invoice(asset, item_code, company, serial_no=None): si = frappe.new_doc("Sales Invoice") @@ -511,7 +589,7 @@ def transfer_asset(args): frappe.db.commit() - frappe.msgprint(_("Asset Movement record {0} created").format("{0}".format(movement_entry.name))) + frappe.msgprint(_("Asset Movement record {0} created").format("{0}").format(movement_entry.name)) @frappe.whitelist() def get_item_details(item_code, asset_category): @@ -531,15 +609,21 @@ def get_item_details(item_code, asset_category): def get_asset_account(account_name, asset=None, asset_category=None, company=None): account = None if asset: - account = get_asset_category_account(asset, account_name, + account = get_asset_category_account(account_name, asset=asset, asset_category = asset_category, company = company) + if not asset and not account: + account = get_asset_category_account(account_name, asset_category = asset_category, company = company) + if not account: account = frappe.get_cached_value('Company', company, account_name) if not account: - frappe.throw(_("Set {0} in asset category {1} or company {2}") - .format(account_name.replace('_', ' ').title(), asset_category, company)) + if not asset_category: + frappe.throw(_("Set {0} in company {1}").format(account_name.replace('_', ' ').title(), company)) + else: + frappe.throw(_("Set {0} in asset category {1} or company {2}") + .format(account_name.replace('_', ' ').title(), asset_category, company)) return account @@ -574,17 +658,43 @@ def make_journal_entry(asset_name): return je -def is_cwip_accounting_disabled(): - return cint(frappe.db.get_single_value("Asset Settings", "disable_cwip_accounting")) +@frappe.whitelist() +def make_asset_movement(assets, purpose=None): + import json + from six import string_types + + if isinstance(assets, string_types): + assets = json.loads(assets) + + if len(assets) == 0: + frappe.throw(_('Atleast one asset has to be selected.')) + + asset_movement = frappe.new_doc("Asset Movement") + asset_movement.quantity = len(assets) + for asset in assets: + asset = frappe.get_doc('Asset', asset.get('name')) + asset_movement.company = asset.get('company') + asset_movement.append("assets", { + 'asset': asset.get('name'), + 'source_location': asset.get('location'), + 'from_employee': asset.get('custodian') + }) + + if asset_movement.get('assets'): + return asset_movement.as_dict() + +def is_cwip_accounting_enabled(asset_category): + return cint(frappe.db.get_value("Asset Category", asset_category, "enable_cwip_accounting")) def get_pro_rata_amt(row, depreciation_amount, from_date, to_date): days = date_diff(to_date, from_date) + months = month_diff(to_date, from_date) total_days = get_total_days(to_date, row.frequency_of_depreciation) - return (depreciation_amount * flt(days)) / flt(total_days), days + return (depreciation_amount * flt(days)) / flt(total_days), days, months def get_total_days(date, frequency): period_start_date = add_months(date, cint(frequency) * -1) - return date_diff(date, period_start_date) \ No newline at end of file + return date_diff(date, period_start_date) diff --git a/erpnext/assets/doctype/asset/asset_list.js b/erpnext/assets/doctype/asset/asset_list.js index 3b95a17afce..02f39e0e7f4 100644 --- a/erpnext/assets/doctype/asset/asset_list.js +++ b/erpnext/assets/doctype/asset/asset_list.js @@ -30,8 +30,24 @@ frappe.listview_settings['Asset'] = { } else if (doc.status === "Draft") { return [__("Draft"), "red", "status,=,Draft"]; - } - + }, + onload: function(me) { + me.page.add_action_item('Make Asset Movement', function() { + const assets = me.get_checked_items(); + frappe.call({ + method: "erpnext.assets.doctype.asset.asset.make_asset_movement", + freeze: true, + args:{ + "assets": assets + }, + callback: function (r) { + if (r.message) { + var doc = frappe.model.sync(r.message)[0]; + frappe.set_route("Form", doc.doctype, doc.name); + } + } + }); + }); }, } \ No newline at end of file diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index 2d23f77014e..ad671ba0f2c 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -22,7 +22,7 @@ def post_depreciation_entries(date=None): def get_depreciable_assets(date): return frappe.db.sql_list("""select a.name from tabAsset a, `tabDepreciation Schedule` ds - where a.name = ds.parent and a.docstatus=1 and ds.schedule_date<=%s + where a.name = ds.parent and a.docstatus=1 and ds.schedule_date<=%s and a.calculate_depreciation = 1 and a.status in ('Submitted', 'Partially Depreciated') and ifnull(ds.journal_entry, '')=''""", date) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index c09b94fa8ea..a56440de3d3 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -7,14 +7,13 @@ import frappe import unittest from frappe.utils import cstr, nowdate, getdate, flt, get_last_day, add_days, add_months from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries, scrap_asset, restore_asset -from erpnext.assets.doctype.asset.asset import make_sales_invoice, make_purchase_invoice +from erpnext.assets.doctype.asset.asset import make_sales_invoice from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice as make_invoice class TestAsset(unittest.TestCase): def setUp(self): set_depreciation_settings_in_company() - remove_prorated_depreciation_schedule() create_asset_data() frappe.db.sql("delete from `tabTax Rule`") @@ -40,15 +39,15 @@ class TestAsset(unittest.TestCase): }) asset.submit() - pi = make_purchase_invoice(asset.name, asset.item_code, asset.gross_purchase_amount, - asset.company, asset.purchase_date) + pi = make_invoice(pr.name) pi.supplier = "_Test Supplier" pi.insert() pi.submit() asset.load_from_db() self.assertEqual(asset.supplier, "_Test Supplier") self.assertEqual(asset.purchase_date, getdate(purchase_date)) - self.assertEqual(asset.purchase_invoice, pi.name) + # Asset won't have reference to PI when purchased through PR + self.assertEqual(asset.purchase_receipt, pr.name) expected_gle = ( ("Asset Received But Not Billed - _TC", 100000.0, 0.0), @@ -61,20 +60,23 @@ class TestAsset(unittest.TestCase): self.assertEqual(gle, expected_gle) pi.cancel() - + asset.cancel() asset.load_from_db() - self.assertEqual(asset.supplier, None) - self.assertEqual(asset.purchase_invoice, None) + pr.load_from_db() + pr.cancel() + self.assertEqual(asset.docstatus, 2) self.assertFalse(frappe.db.get_value("GL Entry", {"voucher_type": "Purchase Invoice", "voucher_no": pi.name})) def test_is_fixed_asset_set(self): + asset = create_asset(is_existing_asset = 1) doc = frappe.new_doc('Purchase Invoice') doc.supplier = '_Test Supplier' doc.append('items', { 'item_code': 'Macbook Pro', - 'qty': 1 + 'qty': 1, + 'asset': asset.name }) doc.set_missing_values() @@ -200,7 +202,6 @@ class TestAsset(unittest.TestCase): self.assertEqual(schedules, expected_schedules) def test_schedule_for_prorated_straight_line_method(self): - set_prorated_depreciation_schedule() pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location") @@ -233,8 +234,6 @@ class TestAsset(unittest.TestCase): self.assertEqual(schedules, expected_schedules) - remove_prorated_depreciation_schedule() - def test_depreciation(self): pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=100000.0, location="Test Location") @@ -484,9 +483,6 @@ class TestAsset(unittest.TestCase): self.assertTrue(asset.finance_books[0].expected_value_after_useful_life >= asset_value_after_full_schedule) def test_cwip_accounting(self): - from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( - make_purchase_invoice as make_purchase_invoice_from_pr) - pr = make_purchase_receipt(item_code="Macbook Pro", qty=1, rate=5000, do_not_submit=True, location="Test Location") @@ -515,13 +511,13 @@ class TestAsset(unittest.TestCase): ("CWIP Account - _TC", 5250.0, 0.0) ) - gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry` + pr_gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry` where voucher_type='Purchase Receipt' and voucher_no = %s order by account""", pr.name) - self.assertEqual(gle, expected_gle) + self.assertEqual(pr_gle, expected_gle) - pi = make_purchase_invoice_from_pr(pr.name) + pi = make_invoice(pr.name) pi.submit() expected_gle = ( @@ -532,11 +528,11 @@ class TestAsset(unittest.TestCase): ("Expenses Included In Asset Valuation - _TC", 0.0, 250.0), ) - gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry` + pi_gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no = %s order by account""", pi.name) - self.assertEqual(gle, expected_gle) + self.assertEqual(pi_gle, expected_gle) asset = frappe.db.get_value('Asset', {'purchase_receipt': pr.name, 'docstatus': 0}, 'name') @@ -565,6 +561,7 @@ class TestAsset(unittest.TestCase): where voucher_type='Asset' and voucher_no = %s order by account""", asset_doc.name) + self.assertEqual(gle, expected_gle) def test_expense_head(self): @@ -575,7 +572,6 @@ class TestAsset(unittest.TestCase): self.assertEquals('Asset Received But Not Billed - _TC', doc.items[0].expense_account) - def create_asset_data(): if not frappe.db.exists("Asset Category", "Computers"): create_asset_category() @@ -596,15 +592,15 @@ def create_asset(**args): asset = frappe.get_doc({ "doctype": "Asset", - "asset_name": "Macbook Pro 1", + "asset_name": args.asset_name or "Macbook Pro 1", "asset_category": "Computers", - "item_code": "Macbook Pro", - "company": "_Test Company", + "item_code": args.item_code or "Macbook Pro", + "company": args.company or"_Test Company", "purchase_date": "2015-01-01", "calculate_depreciation": 0, "gross_purchase_amount": 100000, "expected_value_after_useful_life": 10000, - "warehouse": "_Test Warehouse - _TC", + "warehouse": args.warehouse or "_Test Warehouse - _TC", "available_for_use_date": "2020-06-06", "location": "Test Location", "asset_owner": "Company", @@ -616,6 +612,9 @@ def create_asset(**args): except frappe.DuplicateEntryError: pass + if args.submit: + asset.submit() + return asset def create_asset_category(): @@ -623,6 +622,7 @@ def create_asset_category(): asset_category.asset_category_name = "Computers" asset_category.total_number_of_depreciations = 3 asset_category.frequency_of_depreciation = 3 + asset_category.enable_cwip_accounting = 1 asset_category.append("accounts", { "company_name": "_Test Company", "fixed_asset_account": "_Test Fixed Asset - _TC", @@ -632,6 +632,8 @@ def create_asset_category(): asset_category.insert() def create_fixed_asset_item(): + meta = frappe.get_meta('Asset') + naming_series = meta.get_field("naming_series").options.splitlines()[0] or 'ACC-ASS-.YYYY.-' try: frappe.get_doc({ "doctype": "Item", @@ -642,7 +644,9 @@ def create_fixed_asset_item(): "item_group": "All Item Groups", "stock_uom": "Nos", "is_stock_item": 0, - "is_fixed_asset": 1 + "is_fixed_asset": 1, + "auto_create_assets": 1, + "asset_naming_series": naming_series }).insert() except frappe.DuplicateEntryError: pass @@ -656,19 +660,4 @@ def set_depreciation_settings_in_company(): company.save() # Enable booking asset depreciation entry automatically - frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1) - -def remove_prorated_depreciation_schedule(): - asset_settings = frappe.get_doc("Asset Settings", "Asset Settings") - asset_settings.schedule_based_on_fiscal_year = 0 - asset_settings.save() - - frappe.db.commit() - -def set_prorated_depreciation_schedule(): - asset_settings = frappe.get_doc("Asset Settings", "Asset Settings") - asset_settings.schedule_based_on_fiscal_year = 1 - asset_settings.number_of_days_in_fiscal_year = 360 - asset_settings.save() - - frappe.db.commit() + frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1) \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_category/asset_category.json b/erpnext/assets/doctype/asset_category/asset_category.json index 882cbe2eaa8..7483b41d4de 100644 --- a/erpnext/assets/doctype/asset_category/asset_category.json +++ b/erpnext/assets/doctype/asset_category/asset_category.json @@ -1,284 +1,115 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:asset_category_name", - "beta": 0, - "creation": "2016-03-01 17:41:39.778765", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 0, - "engine": "InnoDB", + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:asset_category_name", + "creation": "2016-03-01 17:41:39.778765", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "asset_category_name", + "column_break_3", + "depreciation_options", + "enable_cwip_accounting", + "finance_book_detail", + "finance_books", + "section_break_2", + "accounts" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "asset_category_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Asset Category Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "asset_category_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Asset Category Name", + "reqd": 1, + "unique": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "finance_book_detail", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Finance Book Detail", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "finance_book_detail", + "fieldtype": "Section Break", + "label": "Finance Book Detail" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "finance_books", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Finance Books", - "length": 0, - "no_copy": 0, - "options": "Asset Finance Book", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "finance_books", + "fieldtype": "Table", + "label": "Finance Books", + "options": "Asset Finance Book" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_2", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Accounts", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "section_break_2", + "fieldtype": "Section Break", + "label": "Accounts" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "accounts", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Accounts", - "length": 0, - "no_copy": 0, - "options": "Asset Category Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "accounts", + "fieldtype": "Table", + "label": "Accounts", + "options": "Asset Category Account", + "reqd": 1 + }, + { + "fieldname": "depreciation_options", + "fieldtype": "Section Break", + "label": "Depreciation Options" + }, + { + "default": "0", + "fieldname": "enable_cwip_accounting", + "fieldtype": "Check", + "label": "Enable Capital Work in Progress Accounting" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-05-12 14:56:04.116425", - "modified_by": "Administrator", - "module": "Assets", - "name": "Asset Category", - "name_case": "", - "owner": "Administrator", + ], + "modified": "2019-10-11 12:19:59.759136", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Category", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Quality Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Quality Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 + ], + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_category/asset_category.py b/erpnext/assets/doctype/asset_category/asset_category.py index bbdc6ec2cfa..fc08841be99 100644 --- a/erpnext/assets/doctype/asset_category/asset_category.py +++ b/erpnext/assets/doctype/asset_category/asset_category.py @@ -10,20 +10,27 @@ from frappe.model.document import Document class AssetCategory(Document): def validate(self): + self.validate_finance_books() + + def validate_finance_books(self): for d in self.finance_books: for field in ("Total Number of Depreciations", "Frequency of Depreciation"): if cint(d.get(frappe.scrub(field)))<1: frappe.throw(_("Row {0}: {1} must be greater than 0").format(d.idx, field), frappe.MandatoryError) @frappe.whitelist() -def get_asset_category_account(asset, fieldname, account=None, asset_category = None, company = None): - if not asset_category and company: +def get_asset_category_account(fieldname, item=None, asset=None, account=None, asset_category = None, company = None): + if item and frappe.db.get_value("Item", item, "is_fixed_asset"): + asset_category = frappe.db.get_value("Item", item, ["asset_category"]) + + elif not asset_category or not company: if account: if frappe.db.get_value("Account", account, "account_type") != "Fixed Asset": account=None if not account: - asset_category, company = frappe.db.get_value("Asset", asset, ["asset_category", "company"]) + asset_details = frappe.db.get_value("Asset", asset, ["asset_category", "company"]) + asset_category, company = asset_details or [None, None] account = frappe.db.get_value("Asset Category Account", filters={"parent": asset_category, "company_name": company}, fieldname=fieldname) diff --git a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py index 735302a0c3b..6c2fd67a9af 100644 --- a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py @@ -73,8 +73,10 @@ def create_asset_data(): 'doctype': 'Location', 'location_name': 'Test Location' }).insert() - + if not frappe.db.exists("Item", "Photocopier"): + meta = frappe.get_meta('Asset') + naming_series = meta.get_field("naming_series").options frappe.get_doc({ "doctype": "Item", "item_code": "Photocopier", @@ -83,7 +85,9 @@ def create_asset_data(): "company": "_Test Company", "is_fixed_asset": 1, "is_stock_item": 0, - "asset_category": "Equipment" + "asset_category": "Equipment", + "auto_create_assets": 1, + "asset_naming_series": naming_series }).insert() def create_maintenance_team(): diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.js b/erpnext/assets/doctype/asset_movement/asset_movement.js index 7ef6461b5a3..06d8879091c 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.js +++ b/erpnext/assets/doctype/asset_movement/asset_movement.js @@ -2,27 +2,101 @@ // For license information, please see license.txt frappe.ui.form.on('Asset Movement', { - select_serial_no: function(frm) { - if (frm.doc.select_serial_no) { - let serial_no = frm.doc.serial_no - ? frm.doc.serial_no + '\n' + frm.doc.select_serial_no : frm.doc.select_serial_no; - frm.set_value("serial_no", serial_no); - frm.set_value("quantity", serial_no.split('\n').length); - } - }, - - serial_no: function(frm) { - const qty = frm.doc.serial_no ? frm.doc.serial_no.split('\n').length : 0; - frm.set_value("quantity", qty); - }, - - setup: function(frm) { - frm.set_query("select_serial_no", function() { + setup: (frm) => { + frm.set_query("to_employee", "assets", (doc) => { return { filters: { - "asset": frm.doc.asset + company: doc.company } }; + }) + frm.set_query("from_employee", "assets", (doc) => { + return { + filters: { + company: doc.company + } + }; + }) + frm.set_query("reference_name", (doc) => { + return { + filters: { + company: doc.company, + docstatus: 1 + } + }; + }) + frm.set_query("reference_doctype", () => { + return { + filters: { + name: ["in", ["Purchase Receipt", "Purchase Invoice"]] + } + }; + }), + frm.set_query("asset", "assets", () => { + return { + filters: { + status: ["not in", ["Draft"]] + } + } + }) + }, + + onload: (frm) => { + frm.trigger('set_required_fields'); + }, + + purpose: (frm) => { + frm.trigger('set_required_fields'); + }, + + set_required_fields: (frm, cdt, cdn) => { + let fieldnames_to_be_altered; + if (frm.doc.purpose === 'Transfer') { + fieldnames_to_be_altered = { + target_location: { read_only: 0, reqd: 1 }, + source_location: { read_only: 1, reqd: 1 }, + from_employee: { read_only: 1, reqd: 0 }, + to_employee: { read_only: 1, reqd: 0 } + }; + } + else if (frm.doc.purpose === 'Receipt') { + fieldnames_to_be_altered = { + target_location: { read_only: 0, reqd: 1 }, + source_location: { read_only: 1, reqd: 0 }, + from_employee: { read_only: 0, reqd: 1 }, + to_employee: { read_only: 1, reqd: 0 } + }; + } + else if (frm.doc.purpose === 'Issue') { + fieldnames_to_be_altered = { + target_location: { read_only: 1, reqd: 0 }, + source_location: { read_only: 1, reqd: 1 }, + from_employee: { read_only: 1, reqd: 0 }, + to_employee: { read_only: 0, reqd: 1 } + }; + } + Object.keys(fieldnames_to_be_altered).forEach(fieldname => { + let property_to_be_altered = fieldnames_to_be_altered[fieldname]; + Object.keys(property_to_be_altered).forEach(property => { + let value = property_to_be_altered[property]; + frm.set_df_property(fieldname, property, value, cdn, 'assets'); + }); }); + frm.refresh_field('assets'); } }); + +frappe.ui.form.on('Asset Movement Item', { + asset: function(frm, cdt, cdn) { + // on manual entry of an asset auto sets their source location / employee + const asset_name = locals[cdt][cdn].asset; + if (asset_name){ + frappe.db.get_doc('Asset', asset_name).then((asset_doc) => { + if(asset_doc.location) frappe.model.set_value(cdt, cdn, 'source_location', asset_doc.location); + if(asset_doc.custodian) frappe.model.set_value(cdt, cdn, 'from_employee', asset_doc.custodian); + }).catch((err) => { + console.log(err); // eslint-disable-line + }); + } + } +}); \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.json b/erpnext/assets/doctype/asset_movement/asset_movement.json index 68076e1f743..3472ab5d7da 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.json +++ b/erpnext/assets/doctype/asset_movement/asset_movement.json @@ -1,26 +1,19 @@ { "allow_import": 1, - "autoname": "naming_series:", + "autoname": "format:ACC-ASM-{YYYY}-{#####}", "creation": "2016-04-25 18:00:23.559973", "doctype": "DocType", + "engine": "InnoDB", "field_order": [ - "naming_series", "company", "purpose", - "asset", - "transaction_date", "column_break_4", - "quantity", - "select_serial_no", - "serial_no", - "section_break_7", - "source_location", - "target_location", - "column_break_10", - "from_employee", - "to_employee", + "transaction_date", + "section_break_10", + "assets", "reference", "reference_doctype", + "column_break_9", "reference_name", "amended_from" ], @@ -36,23 +29,12 @@ "reqd": 1 }, { - "default": "Transfer", "fieldname": "purpose", "fieldtype": "Select", "label": "Purpose", "options": "\nIssue\nReceipt\nTransfer", "reqd": 1 }, - { - "fieldname": "asset", - "fieldtype": "Link", - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Asset", - "options": "Asset", - "reqd": 1 - }, { "fieldname": "transaction_date", "fieldtype": "Datetime", @@ -65,56 +47,7 @@ "fieldtype": "Column Break" }, { - "fieldname": "quantity", - "fieldtype": "Float", - "label": "Quantity" - }, - { - "fieldname": "select_serial_no", - "fieldtype": "Link", - "label": "Select Serial No", - "options": "Serial No" - }, - { - "fieldname": "serial_no", - "fieldtype": "Small Text", - "label": "Serial No" - }, - { - "fieldname": "section_break_7", - "fieldtype": "Section Break" - }, - { - "fieldname": "source_location", - "fieldtype": "Link", - "label": "Source Location", - "options": "Location" - }, - { - "fieldname": "target_location", - "fieldtype": "Link", - "label": "Target Location", - "options": "Location" - }, - { - "fieldname": "column_break_10", - "fieldtype": "Column Break" - }, - { - "fieldname": "from_employee", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "From Employee", - "options": "Employee" - }, - { - "fieldname": "to_employee", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "To Employee", - "options": "Employee" - }, - { + "collapsible": 1, "fieldname": "reference", "fieldtype": "Section Break", "label": "Reference" @@ -122,18 +55,16 @@ { "fieldname": "reference_doctype", "fieldtype": "Link", - "label": "Reference DocType", + "label": "Reference Document Type", "no_copy": 1, - "options": "DocType", - "read_only": 1 + "options": "DocType" }, { "fieldname": "reference_name", "fieldtype": "Dynamic Link", - "label": "Reference Name", + "label": "Reference Document Name", "no_copy": 1, - "options": "reference_doctype", - "read_only": 1 + "options": "reference_doctype" }, { "fieldname": "amended_from", @@ -145,16 +76,23 @@ "read_only": 1 }, { - "default": "ACC-ASM-.YYYY.-", - "fieldname": "naming_series", - "fieldtype": "Select", - "label": "Series", - "options": "ACC-ASM-.YYYY.-", + "fieldname": "section_break_10", + "fieldtype": "Section Break" + }, + { + "fieldname": "assets", + "fieldtype": "Table", + "label": "Assets", + "options": "Asset Movement Item", "reqd": 1 + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" } ], "is_submittable": 1, - "modified": "2019-09-16 16:27:53.887634", + "modified": "2019-11-23 13:28:47.256935", "modified_by": "Administrator", "module": "Assets", "name": "Asset Movement", diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.py b/erpnext/assets/doctype/asset_movement/asset_movement.py index a1d3308b4d0..3a08baa714e 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.py +++ b/erpnext/assets/doctype/asset_movement/asset_movement.py @@ -5,101 +5,142 @@ from __future__ import unicode_literals import frappe from frappe import _ -from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos from frappe.model.document import Document class AssetMovement(Document): def validate(self): self.validate_asset() self.validate_location() + self.validate_employee() def validate_asset(self): - status, company = frappe.db.get_value("Asset", self.asset, ["status", "company"]) - if self.purpose == 'Transfer' and status in ("Draft", "Scrapped", "Sold"): - frappe.throw(_("{0} asset cannot be transferred").format(status)) + for d in self.assets: + status, company = frappe.db.get_value("Asset", d.asset, ["status", "company"]) + if self.purpose == 'Transfer' and status in ("Draft", "Scrapped", "Sold"): + frappe.throw(_("{0} asset cannot be transferred").format(status)) - if company != self.company: - frappe.throw(_("Asset {0} does not belong to company {1}").format(self.asset, self.company)) + if company != self.company: + frappe.throw(_("Asset {0} does not belong to company {1}").format(d.asset, self.company)) - if self.serial_no and len(get_serial_nos(self.serial_no)) != self.quantity: - frappe.throw(_("Number of serial nos and quantity must be the same")) - - if not(self.source_location or self.target_location or self.from_employee or self.to_employee): - frappe.throw(_("Either location or employee must be required")) - - if (not self.serial_no and - frappe.db.get_value('Serial No', {'asset': self.asset}, 'name')): - frappe.throw(_("Serial no is required for the asset {0}").format(self.asset)) + if not (d.source_location or d.target_location or d.from_employee or d.to_employee): + frappe.throw(_("Either location or employee must be required")) def validate_location(self): - if self.purpose in ['Transfer', 'Issue']: - if not self.serial_no and not (self.from_employee or self.to_employee): - self.source_location = frappe.db.get_value("Asset", self.asset, "location") + for d in self.assets: + if self.purpose in ['Transfer', 'Issue']: + if not d.source_location: + d.source_location = frappe.db.get_value("Asset", d.asset, "location") - if self.purpose == 'Issue' and not (self.source_location or self.from_employee): - frappe.throw(_("Source Location is required for the asset {0}").format(self.asset)) + if not d.source_location: + frappe.throw(_("Source Location is required for the Asset {0}").format(d.asset)) - if self.serial_no and self.source_location: - s_nos = get_serial_nos(self.serial_no) - serial_nos = frappe.db.sql_list(""" select name from `tabSerial No` where location != '%s' - and name in (%s)""" %(self.source_location, ','.join(['%s'] * len(s_nos))), tuple(s_nos)) + if d.source_location: + current_location = frappe.db.get_value("Asset", d.asset, "location") - if serial_nos: - frappe.throw(_("Serial nos {0} does not belongs to the location {1}"). - format(','.join(serial_nos), self.source_location)) + if current_location != d.source_location: + frappe.throw(_("Asset {0} does not belongs to the location {1}"). + format(d.asset, d.source_location)) + + if self.purpose == 'Issue': + if d.target_location: + frappe.throw(_("Issuing cannot be done to a location. \ + Please enter employee who has issued Asset {0}").format(d.asset), title="Incorrect Movement Purpose") + if not d.to_employee: + frappe.throw(_("Employee is required while issuing Asset {0}").format(d.asset)) + + if self.purpose == 'Transfer': + if d.to_employee: + frappe.throw(_("Transferring cannot be done to an Employee. \ + Please enter location where Asset {0} has to be transferred").format( + d.asset), title="Incorrect Movement Purpose") + if not d.target_location: + frappe.throw(_("Target Location is required while transferring Asset {0}").format(d.asset)) + if d.source_location == d.target_location: + frappe.throw(_("Source and Target Location cannot be same")) + + if self.purpose == 'Receipt': + # only when asset is bought and first entry is made + if not d.source_location and not (d.target_location or d.to_employee): + frappe.throw(_("Target Location or To Employee is required while receiving Asset {0}").format(d.asset)) + elif d.source_location: + # when asset is received from an employee + if d.target_location and not d.from_employee: + frappe.throw(_("From employee is required while receiving Asset {0} to a target location").format(d.asset)) + if d.from_employee and not d.target_location: + frappe.throw(_("Target Location is required while receiving Asset {0} from an employee").format(d.asset)) + if d.to_employee and d.target_location: + frappe.throw(_("Asset {0} cannot be received at a location and \ + given to employee in a single movement").format(d.asset)) - if self.source_location and self.source_location == self.target_location and self.purpose == 'Transfer': - frappe.throw(_("Source and Target Location cannot be same")) + def validate_employee(self): + for d in self.assets: + if d.from_employee: + current_custodian = frappe.db.get_value("Asset", d.asset, "custodian") - if self.purpose == 'Receipt' and not (self.target_location or self.to_employee): - frappe.throw(_("Target Location is required for the asset {0}").format(self.asset)) + if current_custodian != d.from_employee: + frappe.throw(_("Asset {0} does not belongs to the custodian {1}"). + format(d.asset, d.from_employee)) + + if d.to_employee and frappe.db.get_value("Employee", d.to_employee, "company") != self.company: + frappe.throw(_("Employee {0} does not belongs to the company {1}"). + format(d.to_employee, self.company)) def on_submit(self): self.set_latest_location_in_asset() + + def before_cancel(self): + self.validate_last_movement() def on_cancel(self): self.set_latest_location_in_asset() + + def validate_last_movement(self): + for d in self.assets: + auto_gen_movement_entry = frappe.db.sql( + """ + SELECT asm.name + FROM `tabAsset Movement Item` asm_item, `tabAsset Movement` asm + WHERE + asm.docstatus=1 and + asm_item.parent=asm.name and + asm_item.asset=%s and + asm.company=%s and + asm_item.source_location is NULL and + asm.purpose=%s + ORDER BY + asm.transaction_date asc + """, (d.asset, self.company, 'Receipt'), as_dict=1) + if auto_gen_movement_entry and auto_gen_movement_entry[0].get('name') == self.name: + frappe.throw(_('{0} will be cancelled automatically on asset cancellation as it was \ + auto generated for Asset {1}').format(self.name, d.asset)) def set_latest_location_in_asset(self): - location, employee = '', '' + current_location, current_employee = '', '' cond = "1=1" - args = { - 'asset': self.asset, - 'company': self.company - } + for d in self.assets: + args = { + 'asset': d.asset, + 'company': self.company + } - if self.serial_no: - cond = "serial_no like %(txt)s" - args.update({ - 'txt': "%%%s%%" % self.serial_no - }) + # latest entry corresponds to current document's location, employee when transaction date > previous dates + # In case of cancellation it corresponds to previous latest document's location, employee + latest_movement_entry = frappe.db.sql( + """ + SELECT asm_item.target_location, asm_item.to_employee + FROM `tabAsset Movement Item` asm_item, `tabAsset Movement` asm + WHERE + asm_item.parent=asm.name and + asm_item.asset=%(asset)s and + asm.company=%(company)s and + asm.docstatus=1 and {0} + ORDER BY + asm.transaction_date desc limit 1 + """.format(cond), args) + if latest_movement_entry: + current_location = latest_movement_entry[0][0] + current_employee = latest_movement_entry[0][1] - latest_movement_entry = frappe.db.sql("""select target_location, to_employee from `tabAsset Movement` - where asset=%(asset)s and docstatus=1 and company=%(company)s and {0} - order by transaction_date desc limit 1""".format(cond), args) - - if latest_movement_entry: - location = latest_movement_entry[0][0] - employee = latest_movement_entry[0][1] - elif self.purpose in ['Transfer', 'Receipt']: - movement_entry = frappe.db.sql("""select source_location, from_employee from `tabAsset Movement` - where asset=%(asset)s and docstatus=2 and company=%(company)s and {0} - order by transaction_date asc limit 1""".format(cond), args) - if movement_entry: - location = movement_entry[0][0] - employee = movement_entry[0][1] - - if not self.serial_no: - frappe.db.set_value("Asset", self.asset, "location", location) - - if not employee and self.purpose in ['Receipt', 'Transfer']: - employee = self.to_employee - - if self.serial_no: - for d in get_serial_nos(self.serial_no): - if (location or (self.purpose == 'Issue' and self.source_location)): - frappe.db.set_value('Serial No', d, 'location', location) - - if employee or self.docstatus==2 or self.purpose == 'Issue': - frappe.db.set_value('Serial No', d, 'employee', employee) + frappe.db.set_value('Asset', d.asset, 'location', current_location) + frappe.db.set_value('Asset', d.asset, 'custodian', current_employee) diff --git a/erpnext/assets/doctype/asset_movement/test_asset_movement.py b/erpnext/assets/doctype/asset_movement/test_asset_movement.py index 4d853374455..c3755a3fb9a 100644 --- a/erpnext/assets/doctype/asset_movement/test_asset_movement.py +++ b/erpnext/assets/doctype/asset_movement/test_asset_movement.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe import unittest +import erpnext from erpnext.stock.doctype.item.test_item import make_item from frappe.utils import now, nowdate, get_last_day, add_days from erpnext.assets.doctype.asset.test_asset import create_asset_data @@ -16,7 +17,6 @@ class TestAssetMovement(unittest.TestCase): def setUp(self): create_asset_data() make_location() - make_serialized_item() def test_movement(self): pr = make_purchase_receipt(item_code="Macbook Pro", @@ -38,68 +38,72 @@ class TestAssetMovement(unittest.TestCase): if asset.docstatus == 0: asset.submit() + + # check asset movement is created if not frappe.db.exists("Location", "Test Location 2"): frappe.get_doc({ 'doctype': 'Location', 'location_name': 'Test Location 2' }).insert() - movement1 = create_asset_movement(asset= asset.name, purpose = 'Transfer', - company=asset.company, source_location="Test Location", target_location="Test Location 2") + movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company, + assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'target_location': 'Test Location 2'}], + reference_doctype = 'Purchase Receipt', reference_name = pr.name) self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location 2") - movement2 = create_asset_movement(asset= asset.name, purpose = 'Transfer', - company=asset.company, source_location = "Test Location 2", target_location="Test Location") + movement2 = create_asset_movement(purpose = 'Transfer', company = asset.company, + assets = [{ 'asset': asset.name , 'source_location': 'Test Location 2', 'target_location': 'Test Location'}], + reference_doctype = 'Purchase Receipt', reference_name = pr.name) self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location") movement1.cancel() self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location") - movement2.cancel() - self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location") - - def test_movement_for_serialized_asset(self): - asset_item = "Test Serialized Asset Item" - pr = make_purchase_receipt(item_code=asset_item, rate = 1000, qty=3, location = "Mumbai") - asset_name = frappe.db.get_value('Asset', {'purchase_receipt': pr.name}, 'name') - + employee = make_employee("testassetmovemp@example.com", company="_Test Company") + movement3 = create_asset_movement(purpose = 'Issue', company = asset.company, + assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'to_employee': employee}], + reference_doctype = 'Purchase Receipt', reference_name = pr.name) + + # after issuing asset should belong to an employee not at a location + self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), None) + self.assertEqual(frappe.db.get_value("Asset", asset.name, "custodian"), employee) + + def test_last_movement_cancellation(self): + pr = make_purchase_receipt(item_code="Macbook Pro", + qty=1, rate=100000.0, location="Test Location") + + asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name') asset = frappe.get_doc('Asset', asset_name) - month_end_date = get_last_day(nowdate()) - asset.available_for_use_date = nowdate() if nowdate() != month_end_date else add_days(nowdate(), -15) - asset.calculate_depreciation = 1 + asset.available_for_use_date = '2020-06-06' + asset.purchase_date = '2020-06-06' asset.append("finance_books", { - "expected_value_after_useful_life": 200, + "expected_value_after_useful_life": 10000, + "next_depreciation_date": "2020-12-31", "depreciation_method": "Straight Line", "total_number_of_depreciations": 3, "frequency_of_depreciation": 10, - "depreciation_start_date": month_end_date + "depreciation_start_date": "2020-06-06" }) - asset.submit() - serial_nos = frappe.db.get_value('Asset Movement', {'reference_name': pr.name}, 'serial_no') + if asset.docstatus == 0: + asset.submit() + + if not frappe.db.exists("Location", "Test Location 2"): + frappe.get_doc({ + 'doctype': 'Location', + 'location_name': 'Test Location 2' + }).insert() + + movement = frappe.get_doc({'doctype': 'Asset Movement', 'reference_name': pr.name }) + self.assertRaises(frappe.ValidationError, movement.cancel) - mov1 = create_asset_movement(asset=asset_name, purpose = 'Transfer', - company=asset.company, source_location = "Mumbai", target_location="Pune", serial_no=serial_nos) - self.assertEqual(mov1.target_location, "Pune") + movement1 = create_asset_movement(purpose = 'Transfer', company = asset.company, + assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'target_location': 'Test Location 2'}], + reference_doctype = 'Purchase Receipt', reference_name = pr.name) + self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location 2") - serial_no = frappe.db.get_value('Serial No', {'asset': asset_name}, 'name') - - employee = make_employee("testassetemp@example.com") - create_asset_movement(asset=asset_name, purpose = 'Transfer', - company=asset.company, serial_no=serial_no, to_employee=employee) - - self.assertEqual(frappe.db.get_value('Serial No', serial_no, 'employee'), employee) - - create_asset_movement(asset=asset_name, purpose = 'Transfer', company=asset.company, - serial_no=serial_no, from_employee=employee, to_employee="_T-Employee-00001") - - self.assertEqual(frappe.db.get_value('Serial No', serial_no, 'location'), "Pune") - - mov4 = create_asset_movement(asset=asset_name, purpose = 'Transfer', - company=asset.company, source_location = "Pune", target_location="Nagpur", serial_no=serial_nos) - self.assertEqual(mov4.target_location, "Nagpur") - self.assertEqual(frappe.db.get_value('Serial No', serial_no, 'location'), "Nagpur") - self.assertEqual(frappe.db.get_value('Serial No', serial_no, 'employee'), "_T-Employee-00001") + movement1.cancel() + self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location") def create_asset_movement(**args): args = frappe._dict(args) @@ -109,22 +113,14 @@ def create_asset_movement(**args): movement = frappe.new_doc("Asset Movement") movement.update({ - "asset": args.asset, + "assets": args.assets, "transaction_date": args.transaction_date, - "target_location": args.target_location, "company": args.company, 'purpose': args.purpose or 'Receipt', - 'serial_no': args.serial_no, - 'quantity': len(get_serial_nos(args.serial_no)) if args.serial_no else 1, - 'from_employee': "_T-Employee-00001" or args.from_employee, - 'to_employee': args.to_employee + 'reference_doctype': args.reference_doctype, + 'reference_name': args.reference_name }) - if args.source_location: - movement.update({ - 'source_location': args.source_location - }) - movement.insert() movement.submit() @@ -137,33 +133,3 @@ def make_location(): 'doctype': 'Location', 'location_name': location }).insert(ignore_permissions = True) - -def make_serialized_item(): - asset_item = "Test Serialized Asset Item" - - if not frappe.db.exists('Item', asset_item): - asset_category = frappe.get_all('Asset Category') - - if asset_category: - asset_category = asset_category[0].name - - if not asset_category: - doc = frappe.get_doc({ - 'doctype': 'Asset Category', - 'asset_category_name': 'Test Asset Category', - 'depreciation_method': 'Straight Line', - 'total_number_of_depreciations': 12, - 'frequency_of_depreciation': 1, - 'accounts': [{ - 'company_name': '_Test Company', - 'fixed_asset_account': '_Test Fixed Asset - _TC', - 'accumulated_depreciation_account': 'Depreciation - _TC', - 'depreciation_expense_account': 'Depreciation - _TC' - }] - }).insert() - - asset_category = doc.name - - make_item(asset_item, {'is_stock_item':0, - 'stock_uom': 'Box', 'is_fixed_asset': 1, 'has_serial_no': 1, - 'asset_category': asset_category, 'serial_no_series': 'ABC.###'}) diff --git a/erpnext/setup/doctype/setup_progress/__init__.py b/erpnext/assets/doctype/asset_movement_item/__init__.py similarity index 100% rename from erpnext/setup/doctype/setup_progress/__init__.py rename to erpnext/assets/doctype/asset_movement_item/__init__.py diff --git a/erpnext/assets/doctype/asset_movement_item/asset_movement_item.json b/erpnext/assets/doctype/asset_movement_item/asset_movement_item.json new file mode 100644 index 00000000000..994c3c09898 --- /dev/null +++ b/erpnext/assets/doctype/asset_movement_item/asset_movement_item.json @@ -0,0 +1,86 @@ +{ + "creation": "2019-10-07 18:49:00.737806", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "company", + "asset", + "source_location", + "from_employee", + "column_break_2", + "asset_name", + "target_location", + "to_employee" + ], + "fields": [ + { + "fieldname": "asset", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Asset", + "options": "Asset", + "reqd": 1 + }, + { + "fetch_from": "asset.asset_name", + "fieldname": "asset_name", + "fieldtype": "Data", + "label": "Asset Name", + "read_only": 1 + }, + { + "fieldname": "source_location", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Source Location", + "options": "Location" + }, + { + "fieldname": "target_location", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Target Location", + "options": "Location" + }, + { + "fieldname": "from_employee", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "label": "From Employee", + "options": "Employee" + }, + { + "fieldname": "to_employee", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "label": "To Employee", + "options": "Employee" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "hidden": 1, + "label": "Company", + "options": "Company", + "read_only": 1 + } + ], + "istable": 1, + "modified": "2019-10-09 15:59:08.265141", + "modified_by": "Administrator", + "module": "Assets", + "name": "Asset Movement Item", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_settings/asset_settings.py b/erpnext/assets/doctype/asset_movement_item/asset_movement_item.py similarity index 59% rename from erpnext/assets/doctype/asset_settings/asset_settings.py rename to erpnext/assets/doctype/asset_movement_item/asset_movement_item.py index e303ebd23f9..4c6aaab58a0 100644 --- a/erpnext/assets/doctype/asset_settings/asset_settings.py +++ b/erpnext/assets/doctype/asset_movement_item/asset_movement_item.py @@ -1,9 +1,10 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt from __future__ import unicode_literals +# import frappe from frappe.model.document import Document -class AssetSettings(Document): +class AssetMovementItem(Document): pass diff --git a/erpnext/assets/doctype/asset_settings/asset_settings.js b/erpnext/assets/doctype/asset_settings/asset_settings.js deleted file mode 100644 index 3b421486c38..00000000000 --- a/erpnext/assets/doctype/asset_settings/asset_settings.js +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Asset Settings', { -}); diff --git a/erpnext/assets/doctype/asset_settings/asset_settings.json b/erpnext/assets/doctype/asset_settings/asset_settings.json deleted file mode 100644 index edc5ce169ca..00000000000 --- a/erpnext/assets/doctype/asset_settings/asset_settings.json +++ /dev/null @@ -1,148 +0,0 @@ -{ - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-01-03 10:30:32.983381", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "depreciation_options", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Depreciation Options", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "disable_cwip_accounting", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Disable CWIP Accounting", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "modified": "2019-05-26 18:31:19.930563", - "modified_by": "Administrator", - "module": "Assets", - "name": "Asset Settings", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file diff --git a/erpnext/assets/doctype/asset_settings/test_asset_settings.js b/erpnext/assets/doctype/asset_settings/test_asset_settings.js deleted file mode 100644 index eac2c928f37..00000000000 --- a/erpnext/assets/doctype/asset_settings/test_asset_settings.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Asset Settings", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Asset Settings - () => frappe.tests.make('Asset Settings', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/erpnext/assets/doctype/asset_settings/test_asset_settings.py b/erpnext/assets/doctype/asset_settings/test_asset_settings.py deleted file mode 100644 index 75f146a27ee..00000000000 --- a/erpnext/assets/doctype/asset_settings/test_asset_settings.py +++ /dev/null @@ -1,9 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt -from __future__ import unicode_literals - -import unittest - -class TestAssetSettings(unittest.TestCase): - pass diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.json b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.json index a25b4ce82e6..3236e726ded 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.json +++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.json @@ -60,7 +60,8 @@ { "fieldname": "date", "fieldtype": "Date", - "label": "Date" + "label": "Date", + "reqd": 1 }, { "fieldname": "current_asset_value", @@ -110,7 +111,7 @@ } ], "is_submittable": 1, - "modified": "2019-05-26 09:46:23.613412", + "modified": "2019-11-22 14:09:25.800375", "modified_by": "Administrator", "module": "Assets", "name": "Asset Value Adjustment", diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py index 56425a0dcb4..155597e8565 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py +++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py @@ -5,12 +5,13 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import flt, getdate, cint, date_diff +from frappe.utils import flt, getdate, cint, date_diff, formatdate from erpnext.assets.doctype.asset.depreciation import get_depreciation_accounts from frappe.model.document import Document class AssetValueAdjustment(Document): def validate(self): + self.validate_date() self.set_difference_amount() self.set_current_asset_value() @@ -23,6 +24,12 @@ class AssetValueAdjustment(Document): frappe.throw(_("Cancel the journal entry {0} first").format(self.journal_entry)) self.reschedule_depreciations(self.current_asset_value) + + def validate_date(self): + asset_purchase_date = frappe.db.get_value('Asset', self.asset, 'purchase_date') + if getdate(self.date) < getdate(asset_purchase_date): + frappe.throw(_("Asset Value Adjustment cannot be posted before Asset's purchase date {0}.") + .format(formatdate(asset_purchase_date)), title="Incorrect Date") def set_difference_amount(self): self.difference_amount = flt(self.current_asset_value - self.new_asset_value) diff --git a/erpnext/assets/doctype/location/location.json b/erpnext/assets/doctype/location/location.json index 5dc13062b26..5ecc72ab913 100644 --- a/erpnext/assets/doctype/location/location.json +++ b/erpnext/assets/doctype/location/location.json @@ -1,580 +1,146 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:location_name", - "beta": 0, "creation": "2018-05-07 12:49:22.595974", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "location_name", + "parent_location", + "cb_details", + "is_container", + "is_group", + "sb_location_details", + "latitude", + "longitude", + "cb_latlong", + "area", + "area_uom", + "sb_geolocation", + "location", + "tree_details", + "lft", + "rgt", + "old_parent" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "location_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Location Name", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, "unique": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "parent_location", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Parent Location", - "length": 0, - "no_copy": 0, "options": "Location", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "cb_details", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "description": "Check if it is a hydroponic unit", "fieldname": "is_container", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Container", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Is Container" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "is_group", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, - "label": "Is Group", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Is Group" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "sb_location_details", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Location Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Location Details" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "parent_location.latitude", "fieldname": "latitude", "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Latitude", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Latitude" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "parent_location.longitude", "fieldname": "longitude", "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Longitude", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Longitude" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "cb_latlong", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "area", "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Area", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.area", "fieldname": "area_uom", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Area UOM", - "length": 0, - "no_copy": 0, - "options": "UOM", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "UOM" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "sb_geolocation", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "location", "fieldtype": "Geolocation", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Location", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Location" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "tree_details", "fieldtype": "Section Break", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Tree Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Tree Details" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "lft", "fieldtype": "Int", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "lft", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "rgt", "fieldtype": "Int", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "rgt", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "old_parent", "fieldtype": "Data", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Old Parent", - "length": 0, "no_copy": 1, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-07-11 13:36:30.999405", + "links": [], + "modified": "2020-01-28 13:52:22.513425", "modified_by": "Administrator", "module": "Assets", "name": "Location", @@ -582,127 +148,78 @@ "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "System Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Stock User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Accounts User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Stock Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Agriculture Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Agriculture User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 } ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index 426caaad92d..91ce9ce7fe9 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -20,11 +20,32 @@ frappe.query_reports["Fixed Asset Register"] = { default: 'In Location', reqd: 1 }, + { + fieldname:"purchase_date", + label: __("Purchase Date"), + fieldtype: "Date" + }, + { + fieldname:"available_for_use_date", + label: __("Available For Use Date"), + fieldtype: "Date" + }, { fieldname:"finance_book", label: __("Finance Book"), fieldtype: "Link", options: "Finance Book" }, + { + fieldname:"asset_category", + label: __("Asset Category"), + fieldtype: "Link", + options: "Asset Category" + }, + { + fieldname:"is_existing_asset", + label: __("Is Existing Asset"), + fieldtype: "Check" + }, ] }; diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index f395499ad6b..fa2fe7b4a3c 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import cstr +from frappe.utils import cstr, today, flt def execute(filters=None): filters = frappe._dict(filters or {}) @@ -40,6 +40,42 @@ def get_columns(filters): "fieldname": "status", "width": 90 }, + { + "label": _("Purchase Date"), + "fieldtype": "Date", + "fieldname": "purchase_date", + "width": 90 + }, + { + "label": _("Available For Use Date"), + "fieldtype": "Date", + "fieldname": "available_for_use_date", + "width": 90 + }, + { + "label": _("Gross Purchase Amount"), + "fieldname": "gross_purchase_amount", + "options": "Currency", + "width": 90 + }, + { + "label": _("Asset Value"), + "fieldname": "asset_value", + "options": "Currency", + "width": 90 + }, + { + "label": _("Opening Accumulated Depreciation"), + "fieldname": "opening_accumulated_depreciation", + "options": "Currency", + "width": 90 + }, + { + "label": _("Depreciated Amount"), + "fieldname": "depreciated_amount", + "options": "Currency", + "width": 90 + }, { "label": _("Cost Center"), "fieldtype": "Link", @@ -54,25 +90,6 @@ def get_columns(filters): "options": "Department", "width": 100 }, - { - "label": _("Location"), - "fieldtype": "Link", - "fieldname": "location", - "options": "Location", - "width": 100 - }, - { - "label": _("Purchase Date"), - "fieldtype": "Date", - "fieldname": "purchase_date", - "width": 90 - }, - { - "label": _("Gross Purchase Amount"), - "fieldname": "gross_purchase_amount", - "options": "Currency", - "width": 90 - }, { "label": _("Vendor Name"), "fieldtype": "Data", @@ -80,25 +97,29 @@ def get_columns(filters): "width": 100 }, { - "label": _("Available For Use Date"), - "fieldtype": "Date", - "fieldname": "available_for_use_date", - "width": 90 - }, - { - "label": _("Current Value"), - "fieldname": "current_value", - "options": "Currency", - "width": 90 + "label": _("Location"), + "fieldtype": "Link", + "fieldname": "location", + "options": "Location", + "width": 100 }, ] def get_conditions(filters): - conditions = {'docstatus': 1} + conditions = { 'docstatus': 1 } status = filters.status + date = filters.date - if filters.company: + if filters.get('company'): conditions["company"] = filters.company + if filters.get('purchase_date'): + conditions["purchase_date"] = ('<=', filters.get('purchase_date')) + if filters.get('available_for_use_date'): + conditions["available_for_use_date"] = ('<=', filters.get('available_for_use_date')) + if filters.get('is_existing_asset'): + conditions["is_existing_asset"] = filters.get('is_existing_asset') + if filters.get('asset_category'): + conditions["asset_category"] = filters.get('asset_category') # In Store assets are those that are not sold or scrapped operand = 'not in' @@ -114,7 +135,7 @@ def get_data(filters): data = [] conditions = get_conditions(filters) - current_value_map = get_finance_book_value_map(filters.finance_book) + depreciation_amount_map = get_finance_book_value_map(filters) pr_supplier_map = get_purchase_receipt_supplier_map() pi_supplier_map = get_purchase_invoice_supplier_map() @@ -122,10 +143,12 @@ def get_data(filters): filters=conditions, fields=["name", "asset_name", "department", "cost_center", "purchase_receipt", "asset_category", "purchase_date", "gross_purchase_amount", "location", - "available_for_use_date", "status", "purchase_invoice"]) + "available_for_use_date", "status", "purchase_invoice", "opening_accumulated_depreciation"]) for asset in assets_record: - if current_value_map.get(asset.name) is not None: + asset_value = asset.gross_purchase_amount - flt(asset.opening_accumulated_depreciation) \ + - flt(depreciation_amount_map.get(asset.name)) + if asset_value: row = { "asset_id": asset.name, "asset_name": asset.asset_name, @@ -134,23 +157,30 @@ def get_data(filters): "cost_center": asset.cost_center, "vendor_name": pr_supplier_map.get(asset.purchase_receipt) or pi_supplier_map.get(asset.purchase_invoice), "gross_purchase_amount": asset.gross_purchase_amount, + "opening_accumulated_depreciation": asset.opening_accumulated_depreciation, + "depreciated_amount": depreciation_amount_map.get(asset.name) or 0.0, "available_for_use_date": asset.available_for_use_date, "location": asset.location, "asset_category": asset.asset_category, "purchase_date": asset.purchase_date, - "current_value": current_value_map.get(asset.name) + "asset_value": asset_value } data.append(row) return data -def get_finance_book_value_map(finance_book=''): +def get_finance_book_value_map(filters): + date = filters.get('purchase_date') or filters.get('available_for_use_date') or today() + return frappe._dict(frappe.db.sql(''' Select - parent, value_after_depreciation - FROM `tabAsset Finance Book` + parent, SUM(depreciation_amount) + FROM `tabDepreciation Schedule` WHERE - parentfield='finance_books' - AND ifnull(finance_book, '')=%s''', cstr(finance_book))) + parentfield='schedules' + AND schedule_date<=%s + AND journal_entry IS NOT NULL + AND ifnull(finance_book, '')=%s + GROUP BY parent''', (date, cstr(filters.finance_book or '')))) def get_purchase_receipt_supplier_map(): return frappe._dict(frappe.db.sql(''' Select diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index c5fa98da09f..7b1f1354d79 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -18,6 +18,7 @@ frappe.ui.form.on("Purchase Order", { return { filters: { "company": frm.doc.company, + "name": ['!=', frm.doc.supplier_warehouse], "is_group": 0 } } @@ -133,7 +134,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( if (doc.status != "On Hold") { if(flt(doc.per_received, 2) < 100 && allow_receipt) { cur_frm.add_custom_button(__('Receipt'), this.make_purchase_receipt, __('Create')); - if(doc.is_subcontracted==="Yes") { + if(doc.is_subcontracted==="Yes" && me.has_unsupplied_items()) { cur_frm.add_custom_button(__('Material to Supplier'), function() { me.make_stock_entry(); }, __("Transfer")); } @@ -190,6 +191,10 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( set_schedule_date(this.frm); }, + has_unsupplied_items: function() { + return this.frm.doc['supplied_items'].some(item => item.required_qty != item.supplied_qty) + }, + make_stock_entry: function() { var items = $.map(cur_frm.doc.items, function(d) { return d.bom ? d.item_code : false; }); var me = this; @@ -266,7 +271,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( if (me.frm.doc['supplied_items']) { me.frm.doc['supplied_items'].forEach((item, index) => { - if (item.rm_item_code && item.main_item_code) { + if (item.rm_item_code && item.main_item_code && item.required_qty - item.supplied_qty != 0) { me.raw_material_data.push ({ 'name':item.name, 'item_code': item.main_item_code, @@ -283,6 +288,8 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend( }) } + me.dialog.get_field('sub_con_rm_items').check_all_rows() + me.dialog.show() this.dialog.set_primary_action(__('Transfer'), function() { me.values = me.dialog.get_values(); diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 5dce3497829..d82e128735e 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -56,6 +56,7 @@ "section_break_48", "pricing_rules", "raw_material_details", + "set_reserve_warehouse", "supplied_items", "sb_last_purchase", "total_qty", @@ -340,6 +341,7 @@ "fieldname": "contact_email", "fieldtype": "Small Text", "label": "Contact Email", + "options": "Email", "print_hide": 1, "read_only": 1 }, @@ -607,7 +609,7 @@ }, { "fieldname": "other_charges_calculation", - "fieldtype": "Text", + "fieldtype": "Long Text", "label": "Taxes and Charges Calculation", "no_copy": 1, "oldfieldtype": "HTML", @@ -1039,12 +1041,19 @@ "fieldtype": "Link", "label": "Tax Category", "options": "Tax Category" + }, + { + "depends_on": "supplied_items", + "fieldname": "set_reserve_warehouse", + "fieldtype": "Link", + "label": "Set Reserve Warehouse", + "options": "Warehouse" } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, - "modified": "2019-07-11 18:25:49.509343", + "modified": "2020-01-14 18:54:39.694448", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 845ff747d61..f62df20ae1a 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -313,7 +313,7 @@ def item_last_purchase_rate(name, conversion_rate, item_code, conversion_factor= last_purchase_details = get_last_purchase_details(item_code, name) if last_purchase_details: - last_purchase_rate = (last_purchase_details['base_rate'] * (flt(conversion_factor) or 1.0)) / conversion_rate + last_purchase_rate = (last_purchase_details['base_net_rate'] * (flt(conversion_factor) or 1.0)) / conversion_rate return last_purchase_rate else: item_last_purchase_rate = frappe.get_cached_value("Item", item_code, "last_purchase_rate") diff --git a/erpnext/buying/doctype/purchase_order/regional/india.js b/erpnext/buying/doctype/purchase_order/regional/india.js new file mode 100644 index 00000000000..42d3995907f --- /dev/null +++ b/erpnext/buying/doctype/purchase_order/regional/india.js @@ -0,0 +1,3 @@ +{% include "erpnext/regional/india/taxes.js" %} + +erpnext.setup_auto_gst_taxation('Purchase Order'); \ No newline at end of file diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 4506db64051..1712369e60b 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -17,6 +17,8 @@ from erpnext.stock.doctype.material_request.material_request import make_purchas from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry from erpnext.controllers.accounts_controller import update_child_qty_rate from erpnext.controllers.status_updater import OverAllowanceError +from erpnext.manufacturing.doctype.blanket_order.test_blanket_order import make_blanket_order + class TestPurchaseOrder(unittest.TestCase): def test_make_purchase_receipt(self): @@ -116,6 +118,73 @@ class TestPurchaseOrder(unittest.TestCase): self.assertEqual(po.get("items")[0].amount, 1400) self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3) + + def test_add_new_item_in_update_child_qty_rate(self): + po = create_purchase_order(do_not_save=1) + po.items[0].qty = 4 + po.save() + po.submit() + pr = make_pr_against_po(po.name, 2) + + po.load_from_db() + first_item_of_po = po.get("items")[0] + + trans_item = json.dumps([ + { + 'item_code': first_item_of_po.item_code, + 'rate': first_item_of_po.rate, + 'qty': first_item_of_po.qty, + 'docname': first_item_of_po.name + }, + {'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7} + ]) + update_child_qty_rate('Purchase Order', trans_item, po.name) + + po.reload() + self.assertEquals(len(po.get('items')), 2) + self.assertEqual(po.status, 'To Receive and Bill') + + + def test_remove_item_in_update_child_qty_rate(self): + po = create_purchase_order(do_not_save=1) + po.items[0].qty = 4 + po.save() + po.submit() + pr = make_pr_against_po(po.name, 2) + + po.reload() + first_item_of_po = po.get("items")[0] + # add an item + trans_item = json.dumps([ + { + 'item_code': first_item_of_po.item_code, + 'rate': first_item_of_po.rate, + 'qty': first_item_of_po.qty, + 'docname': first_item_of_po.name + }, + {'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7}]) + update_child_qty_rate('Purchase Order', trans_item, po.name) + + po.reload() + # check if can remove received item + trans_item = json.dumps([{'item_code' : '_Test Item', 'rate' : 200, 'qty' : 7, 'docname': po.get("items")[1].name}]) + self.assertRaises(frappe.ValidationError, update_child_qty_rate, 'Purchase Order', trans_item, po.name) + + first_item_of_po = po.get("items")[0] + trans_item = json.dumps([ + { + 'item_code': first_item_of_po.item_code, + 'rate': first_item_of_po.rate, + 'qty': first_item_of_po.qty, + 'docname': first_item_of_po.name + } + ]) + update_child_qty_rate('Purchase Order', trans_item, po.name) + + po.reload() + self.assertEquals(len(po.get('items')), 1) + self.assertEqual(po.status, 'To Receive and Bill') + def test_update_qty(self): po = create_purchase_order() @@ -519,47 +588,62 @@ class TestPurchaseOrder(unittest.TestCase): def test_backflush_based_on_stock_entry(self): item_code = "_Test Subcontracted FG Item 1" make_subcontracted_item(item_code) + make_item('Sub Contracted Raw Material 1', { + 'is_stock_item': 1, + 'is_sub_contracted_item': 1 + }) update_backflush_based_on("Material Transferred for Subcontract") - po = create_purchase_order(item_code=item_code, qty=1, + + order_qty = 5 + po = create_purchase_order(item_code=item_code, qty=order_qty, is_subcontracted="Yes", supplier_warehouse="_Test Warehouse 1 - _TC") - make_stock_entry(target="_Test Warehouse - _TC", qty=10, basic_rate=100) make_stock_entry(target="_Test Warehouse - _TC", item_code="_Test Item Home Desktop 100", qty=10, basic_rate=100) make_stock_entry(target="_Test Warehouse - _TC", item_code = "Test Extra Item 1", qty=100, basic_rate=100) make_stock_entry(target="_Test Warehouse - _TC", item_code = "Test Extra Item 2", qty=10, basic_rate=100) + make_stock_entry(target="_Test Warehouse - _TC", + item_code = "Sub Contracted Raw Material 1", qty=10, basic_rate=100) - rm_item = [ - {"item_code":item_code,"rm_item_code":"_Test Item","item_name":"_Test Item", - "qty":1,"warehouse":"_Test Warehouse - _TC","rate":100,"amount":100,"stock_uom":"Nos"}, + rm_items = [ + {"item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 1","item_name":"_Test Item", + "qty":10,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos"}, {"item_code":item_code,"rm_item_code":"_Test Item Home Desktop 100","item_name":"_Test Item Home Desktop 100", - "qty":2,"warehouse":"_Test Warehouse - _TC","rate":100,"amount":200,"stock_uom":"Nos"}, + "qty":20,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos"}, {"item_code":item_code,"rm_item_code":"Test Extra Item 1","item_name":"Test Extra Item 1", - "qty":1,"warehouse":"_Test Warehouse - _TC","rate":100,"amount":200,"stock_uom":"Nos"}] + "qty":10,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos"}, + {'item_code': item_code, 'rm_item_code': 'Test Extra Item 2', 'stock_uom':'Nos', + 'qty': 10, 'warehouse': '_Test Warehouse - _TC', 'item_name':'Test Extra Item 2'}] - rm_item_string = json.dumps(rm_item) + rm_item_string = json.dumps(rm_items) se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string)) - se.append('items', { - 'item_code': "Test Extra Item 2", - "qty": 1, - "rate": 100, - "s_warehouse": "_Test Warehouse - _TC", - "t_warehouse": "_Test Warehouse 1 - _TC" - }) - se.set_missing_values() se.submit() pr = make_purchase_receipt(po.name) + + received_qty = 2 + # partial receipt + pr.get('items')[0].qty = received_qty pr.save() pr.submit() - se_items = sorted([d.item_code for d in se.get('items')]) - supplied_items = sorted([d.rm_item_code for d in pr.get('supplied_items')]) + transferred_items = sorted([d.item_code for d in se.get('items') if se.purchase_order == po.name]) + issued_items = sorted([d.rm_item_code for d in pr.get('supplied_items')]) + + self.assertEquals(transferred_items, issued_items) + self.assertEquals(pr.get('items')[0].rm_supp_cost, 2000) + + + transferred_rm_map = frappe._dict() + for item in rm_items: + transferred_rm_map[item.get('rm_item_code')] = item + + for item in pr.get('supplied_items'): + self.assertEqual(item.get('required_qty'), (transferred_rm_map[item.get('rm_item_code')].get('qty') / order_qty) * received_qty) - self.assertEquals(se_items, supplied_items) update_backflush_based_on("BOM") def test_advance_payment_entry_unlink_against_purchase_order(self): @@ -605,6 +689,27 @@ class TestPurchaseOrder(unittest.TestCase): po.save() self.assertEqual(po.schedule_date, add_days(nowdate(), 2)) + + def test_po_optional_blanket_order(self): + """ + Expected result: Blanket order Ordered Quantity should only be affected on Purchase Order with against_blanket_order = 1. + Second Purchase Order should not add on to Blanket Orders Ordered Quantity. + """ + + bo = make_blanket_order(blanket_order_type = "Purchasing", quantity = 10, rate = 10) + + po = create_purchase_order(item_code= "_Test Item", qty = 5, against_blanket_order = 1) + po_doc = frappe.get_doc('Purchase Order', po.get('name')) + # To test if the PO has a Blanket Order + self.assertTrue(po_doc.items[0].blanket_order) + + po = create_purchase_order(item_code= "_Test Item", qty = 5, against_blanket_order = 0) + po_doc = frappe.get_doc('Purchase Order', po.get('name')) + # To test if the PO does NOT have a Blanket Order + self.assertEqual(po_doc.items[0].blanket_order, None) + + + def make_pr_against_po(po, received_qty=0): pr = make_purchase_receipt(po) @@ -678,7 +783,8 @@ def create_purchase_order(**args): "qty": args.qty or 10, "rate": args.rate or 500, "schedule_date": add_days(nowdate(), 1), - "include_exploded_items": args.get('include_exploded_items', 1) + "include_exploded_items": args.get('include_exploded_items', 1), + "against_blanket_order": args.against_blanket_order }) if not args.do_not_save: po.insert() diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index 66ad97ac09e..6768dfabfea 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "hash", "creation": "2013-05-24 19:29:06", "doctype": "DocType", @@ -43,6 +44,7 @@ "base_amount", "pricing_rules", "is_free_item", + "is_fixed_asset", "section_break_29", "net_rate", "net_amount", @@ -66,6 +68,7 @@ "supplier_quotation", "supplier_quotation_item", "col_break5", + "against_blanket_order", "blanket_order", "blanket_order_rate", "item_group", @@ -510,6 +513,7 @@ "read_only": 1 }, { + "depends_on": "eval:doc.against_blanket_order", "fieldname": "blanket_order", "fieldtype": "Link", "label": "Blanket Order", @@ -517,6 +521,7 @@ "options": "Blanket Order" }, { + "depends_on": "eval:doc.against_blanket_order", "fieldname": "blanket_order_rate", "fieldtype": "Currency", "label": "Blanket Order Rate", @@ -699,11 +704,26 @@ "fieldtype": "Data", "label": "Manufacturer Part Number", "read_only": 1 + }, + { + "default": "0", + "fieldname": "against_blanket_order", + "fieldtype": "Check", + "label": "Against Blanket Order" + }, + { + "default": "0", + "fetch_from": "item_code.is_fixed_asset", + "fieldname": "is_fixed_asset", + "fieldtype": "Check", + "label": "Is Fixed Asset", + "read_only": 1 } ], "idx": 1, "istable": 1, - "modified": "2019-09-17 22:32:34.703923", + "links": [], + "modified": "2019-12-06 13:17:12.142799", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", diff --git a/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json b/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json index 2e0fc94bc90..6f2fbe5c370 100644 --- a/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json +++ b/erpnext/buying/doctype/purchase_receipt_item_supplied/purchase_receipt_item_supplied.json @@ -1,537 +1,168 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2013-02-22 01:27:42", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", + "creation": "2013-02-22 01:27:42", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "main_item_code", + "rm_item_code", + "description", + "batch_no", + "serial_no", + "col_break1", + "required_qty", + "consumed_qty", + "stock_uom", + "rate", + "amount", + "conversion_factor", + "current_stock", + "reference_name", + "bom_detail_no" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "main_item_code", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Item Code", - "length": 0, - "no_copy": 0, - "oldfieldname": "main_item_code", - "oldfieldtype": "Data", - "options": "Item", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "main_item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Code", + "oldfieldname": "main_item_code", + "oldfieldtype": "Data", + "options": "Item", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "rm_item_code", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Raw Material Item Code", - "length": 0, - "no_copy": 0, - "oldfieldname": "rm_item_code", - "oldfieldtype": "Data", - "options": "Item", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "rm_item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Raw Material Item Code", + "oldfieldname": "rm_item_code", + "oldfieldtype": "Data", + "options": "Item", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "oldfieldname": "description", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "300px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "description", + "fieldtype": "Text Editor", + "in_global_search": 1, + "in_list_view": 1, + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Data", + "print_width": "300px", + "read_only": 1, "width": "300px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "batch_no", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Batch No", - "length": 0, - "no_copy": 1, - "options": "Batch", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "batch_no", + "fieldtype": "Link", + "label": "Batch No", + "no_copy": 1, + "options": "Batch" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "serial_no", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Serial No", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "serial_no", + "fieldtype": "Text", + "label": "Serial No", + "no_copy": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "col_break1", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "col_break1", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "required_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Required Qty", - "length": 0, - "no_copy": 0, - "oldfieldname": "required_qty", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "required_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Required Qty", + "oldfieldname": "required_qty", + "oldfieldtype": "Currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "consumed_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Consumed Qty", - "length": 0, - "no_copy": 0, - "oldfieldname": "consumed_qty", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "consumed_qty", + "fieldtype": "Float", + "label": "Consumed Qty", + "oldfieldname": "consumed_qty", + "oldfieldtype": "Currency", + "read_only": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "stock_uom", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Stock Uom", - "length": 0, - "no_copy": 0, - "oldfieldname": "stock_uom", - "oldfieldtype": "Data", - "options": "UOM", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "stock_uom", + "fieldtype": "Link", + "label": "Stock Uom", + "oldfieldname": "stock_uom", + "oldfieldtype": "Data", + "options": "UOM", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "rate", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rate", - "length": 0, - "no_copy": 0, - "oldfieldname": "rate", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "rate", + "fieldtype": "Currency", + "label": "Rate", + "oldfieldname": "rate", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amount", - "length": 0, - "no_copy": 0, - "oldfieldname": "amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "amount", + "fieldtype": "Currency", + "label": "Amount", + "oldfieldname": "amount", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "conversion_factor", - "fieldtype": "Float", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Conversion Factor", - "length": 0, - "no_copy": 0, - "oldfieldname": "conversion_factor", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "conversion_factor", + "fieldtype": "Float", + "hidden": 1, + "label": "Conversion Factor", + "oldfieldname": "conversion_factor", + "oldfieldtype": "Currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "current_stock", - "fieldtype": "Float", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Current Stock", - "length": 0, - "no_copy": 0, - "oldfieldname": "current_stock", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "current_stock", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Current Stock", + "oldfieldname": "current_stock", + "oldfieldtype": "Currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_name", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Reference Name", - "length": 0, - "no_copy": 0, - "oldfieldname": "reference_name", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "reference_name", + "fieldtype": "Data", + "hidden": 1, + "in_list_view": 1, + "label": "Reference Name", + "oldfieldname": "reference_name", + "oldfieldtype": "Data", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bom_detail_no", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "BOM Detail No", - "length": 0, - "no_copy": 0, - "oldfieldname": "bom_detail_no", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "bom_detail_no", + "fieldtype": "Data", + "hidden": 1, + "in_list_view": 1, + "label": "BOM Detail No", + "oldfieldname": "bom_detail_no", + "oldfieldtype": "Data", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-01-07 16:51:59.536291", - "modified_by": "Administrator", - "module": "Buying", - "name": "Purchase Receipt Item Supplied", - "owner": "wasim@webnotestech.com", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "idx": 1, + "istable": 1, + "modified": "2019-11-21 16:25:29.909112", + "modified_by": "Administrator", + "module": "Buying", + "name": "Purchase Receipt Item Supplied", + "owner": "wasim@webnotestech.com", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js index 9ad06f9b7ef..455bd68ecff 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js @@ -9,7 +9,7 @@ cur_frm.add_fetch('contact', 'email_id', 'email_id') frappe.ui.form.on("Request for Quotation",{ setup: function(frm) { frm.custom_make_buttons = { - 'Supplier Quotation': 'Supplier Quotation' + 'Supplier Quotation': 'Create' } frm.fields_dict["suppliers"].grid.get_field("contact").get_query = function(doc, cdt, cdn) { @@ -134,7 +134,7 @@ frappe.ui.form.on("Request for Quotation",{ if (args.search_type === "Tag" && args.tag) { return frappe.call({ type: "GET", - method: "frappe.desk.tags.get_tagged_docs", + method: "frappe.desk.doctype.tag.tag.get_tagged_docs", args: { "doctype": "Supplier", "tag": args.tag diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index a10ce46e33e..95db33b0f8f 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -344,13 +344,9 @@ def get_item_from_material_requests_based_on_supplier(source_name, target_doc = @frappe.whitelist() def get_supplier_tag(): - data = frappe.db.sql("select _user_tags from `tabSupplier`") - - tags = [] - for tag in data: - tags += filter(bool, tag[0].split(",")) - - tags = list(set(tags)) - - return tags + if not frappe.cache().hget("Supplier", "Tags"): + filters = {"document_type": "Supplier"} + tags = list(set([tag.tag for tag in frappe.get_all("Tag Link", filters=filters, fields=["tag"]) if tag])) + frappe.cache().hset("Supplier", "Tags", tags) + return frappe.cache().hget("Supplier", "Tags") diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py index b6d588ed967..62a04f37b11 100644 --- a/erpnext/buying/doctype/supplier/supplier.py +++ b/erpnext/buying/doctype/supplier/supplier.py @@ -56,3 +56,23 @@ class Supplier(TransactionBase): def after_rename(self, olddn, newdn, merge=False): if frappe.defaults.get_global_default('supp_master_name') == 'Supplier Name': frappe.db.set(self, "supplier_name", newdn) + + def create_onboarding_docs(self, args): + defaults = frappe.defaults.get_defaults() + for i in range(1, args.get('max_count')): + supplier = args.get('supplier_name_' + str(i)) + if supplier: + try: + doc = frappe.get_doc({ + 'doctype': self.doctype, + 'supplier_name': supplier, + 'supplier_group': _('Local'), + 'company': defaults.get('company') + }).insert() + + if args.get('supplier_email_' + str(i)): + from erpnext.selling.doctype.customer.customer import create_contact + create_contact(supplier, 'Supplier', + doc.name, args.get('supplier_email_' + str(i))) + except frappe.NameError: + pass \ No newline at end of file diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index b17bed46c4d..82fc6285bcf 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -1,3060 +1,866 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2013-05-21 16:16:45", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 0, + "actions": [], + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2013-05-21 16:16:45", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "supplier_section", + "title", + "naming_series", + "supplier", + "supplier_name", + "column_break1", + "transaction_date", + "amended_from", + "company", + "address_section", + "supplier_address", + "contact_person", + "address_display", + "contact_display", + "contact_mobile", + "contact_email", + "currency_and_price_list", + "currency", + "conversion_rate", + "cb_price_list", + "buying_price_list", + "price_list_currency", + "plc_conversion_rate", + "ignore_pricing_rule", + "items_section", + "items", + "link_to_mrs", + "pricing_rule_details", + "pricing_rules", + "section_break_22", + "total_qty", + "base_total", + "base_net_total", + "column_break_24", + "total", + "net_total", + "total_net_weight", + "taxes_section", + "tax_category", + "column_break_36", + "shipping_rule", + "section_break_38", + "taxes_and_charges", + "taxes", + "tax_breakup", + "other_charges_calculation", + "totals", + "base_taxes_and_charges_added", + "base_taxes_and_charges_deducted", + "base_total_taxes_and_charges", + "column_break_37", + "taxes_and_charges_added", + "taxes_and_charges_deducted", + "total_taxes_and_charges", + "section_break_41", + "apply_discount_on", + "base_discount_amount", + "column_break_43", + "additional_discount_percentage", + "discount_amount", + "section_break_46", + "base_grand_total", + "base_rounding_adjustment", + "base_in_words", + "base_rounded_total", + "column_break4", + "grand_total", + "rounding_adjustment", + "rounded_total", + "in_words", + "disable_rounded_total", + "terms_section_break", + "tc_name", + "terms", + "printing_settings", + "select_print_heading", + "group_same_items", + "column_break_72", + "letter_head", + "language", + "subscription_section", + "auto_repeat", + "update_auto_repeat_reference", + "more_info", + "status", + "column_break_57", + "is_subcontracted", + "reference", + "opportunity" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "supplier_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "options": "fa fa-user", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "supplier_section", + "fieldtype": "Section Break", + "options": "fa fa-user" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "{supplier_name}", - "fieldname": "title", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Title", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "{supplier_name}", + "fieldname": "title", + "fieldtype": "Data", + "hidden": 1, + "label": "Title", + "no_copy": 1, + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Series", - "length": 0, - "no_copy": 1, - "oldfieldname": "naming_series", - "oldfieldtype": "Select", - "options": "PUR-SQTN-.YYYY.-", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "oldfieldname": "naming_series", + "oldfieldtype": "Select", + "options": "PUR-SQTN-.YYYY.-", + "print_hide": 1, + "reqd": 1, + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "supplier", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Supplier", - "length": 0, - "no_copy": 0, - "oldfieldname": "supplier", - "oldfieldtype": "Link", - "options": "Supplier", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "bold": 1, + "fieldname": "supplier", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Supplier", + "oldfieldname": "supplier", + "oldfieldtype": "Link", + "options": "Supplier", + "print_hide": 1, + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "fetch_from": "supplier.supplier_name", - "fieldname": "supplier_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "bold": 1, + "fetch_from": "supplier.supplier_name", + "fieldname": "supplier_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break1", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "50%", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "column_break1", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "print_width": "50%", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "transaction_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Date", - "length": 0, - "no_copy": 0, - "oldfieldname": "transaction_date", - "oldfieldtype": "Date", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Today", + "fieldname": "transaction_date", + "fieldtype": "Date", + "label": "Date", + "oldfieldname": "transaction_date", + "oldfieldtype": "Date", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 1, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "oldfieldname": "amended_from", - "oldfieldtype": "Data", - "options": "Supplier Quotation", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "amended_from", + "fieldtype": "Link", + "hidden": 1, + "ignore_user_permissions": 1, + "label": "Amended From", + "no_copy": 1, + "oldfieldname": "amended_from", + "oldfieldtype": "Data", + "options": "Supplier Quotation", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Company", - "length": 0, - "no_copy": 0, - "oldfieldname": "company", - "oldfieldtype": "Link", - "options": "Company", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Company", + "oldfieldname": "company", + "oldfieldtype": "Link", + "options": "Company", + "print_hide": 1, + "remember_last_selected_value": 1, + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "address_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Address and Contact", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "address_section", + "fieldtype": "Section Break", + "label": "Address and Contact" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "supplier_address", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Supplier Address", - "length": 0, - "no_copy": 0, - "options": "Address", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "supplier_address", + "fieldtype": "Link", + "label": "Supplier Address", + "options": "Address", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "contact_person", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Contact Person", - "length": 0, - "no_copy": 0, - "options": "Contact", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "contact_person", + "fieldtype": "Link", + "label": "Contact Person", + "options": "Contact", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "address_display", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Address", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "address_display", + "fieldtype": "Small Text", + "label": "Address", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "contact_display", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Contact", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "contact_display", + "fieldtype": "Small Text", + "in_global_search": 1, + "label": "Contact", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "contact_mobile", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Mobile No", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "contact_mobile", + "fieldtype": "Small Text", + "label": "Mobile No", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "contact_email", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Contact Email", - "length": 0, - "no_copy": 0, - "options": "Email", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "contact_email", + "fieldtype": "Data", + "label": "Contact Email", + "options": "Email", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "currency_and_price_list", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Currency and Price List", - "length": 0, - "no_copy": 0, - "options": "fa fa-tag", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "currency_and_price_list", + "fieldtype": "Section Break", + "label": "Currency and Price List", + "options": "fa fa-tag" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "currency", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Currency", - "length": 0, - "no_copy": 0, - "oldfieldname": "currency", - "oldfieldtype": "Select", - "options": "Currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "oldfieldname": "currency", + "oldfieldtype": "Select", + "options": "Currency", + "print_hide": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "conversion_rate", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Exchange Rate", - "length": 0, - "no_copy": 0, - "oldfieldname": "conversion_rate", - "oldfieldtype": "Currency", - "permlevel": 0, - "precision": "9", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "conversion_rate", + "fieldtype": "Float", + "label": "Exchange Rate", + "oldfieldname": "conversion_rate", + "oldfieldtype": "Currency", + "precision": "9", + "print_hide": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "cb_price_list", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "50%", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "cb_price_list", + "fieldtype": "Column Break", + "print_width": "50%", "width": "50%" - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "buying_price_list", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Price List", - "length": 0, - "no_copy": 0, - "options": "Price List", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "buying_price_list", - "fieldname": "price_list_currency", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Price List Currency", - "length": 0, - "no_copy": 0, - "options": "Currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "buying_price_list", - "fieldname": "plc_conversion_rate", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Price List Exchange Rate", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "9", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "ignore_pricing_rule", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Ignore Pricing Rule", - "length": 0, - "no_copy": 1, - "permlevel": 1, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "items_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 1, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "items", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Items", - "length": 0, - "no_copy": 0, - "oldfieldname": "po_details", - "oldfieldtype": "Table", - "options": "Supplier Quotation Item", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.docstatus===0 && (doc.items && doc.items.length)", - "fieldname": "link_to_mrs", - "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Link to material requests", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "pricing_rule_details", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Pricing Rules", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "pricing_rules", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Pricing Rule Detail", - "length": 0, - "no_copy": 0, - "options": "Pricing Rule Detail", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_22", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Quantity", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total (Company Currency)", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_net_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Net Total (Company Currency)", - "length": 0, - "no_copy": 1, - "oldfieldname": "net_total", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_24", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total", - "length": 0, - "no_copy": 0, - "options": "currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "net_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Net Total", - "length": 0, - "no_copy": 0, - "oldfieldname": "net_total_import", - "oldfieldtype": "Currency", - "options": "currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_net_weight", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Net Weight", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "taxes_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Taxes and Charges", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-money", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "tax_category", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Tax Category", - "length": 0, - "no_copy": 0, - "options": "Tax Category", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_36", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "shipping_rule", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Shipping Rule", - "length": 0, - "no_copy": 0, - "options": "Shipping Rule", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_38", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "taxes_and_charges", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Purchase Taxes and Charges Template", - "length": 0, - "no_copy": 1, - "oldfieldname": "purchase_other_charges", - "oldfieldtype": "Link", - "options": "Purchase Taxes and Charges Template", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "taxes", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Purchase Taxes and Charges", - "length": 0, - "no_copy": 0, - "oldfieldname": "purchase_tax_details", - "oldfieldtype": "Table", - "options": "Purchase Taxes and Charges", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "tax_breakup", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Tax Breakup", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "other_charges_calculation", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Taxes and Charges Calculation", - "length": 0, - "no_copy": 1, - "oldfieldtype": "HTML", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "totals", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-money", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_taxes_and_charges_added", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Taxes and Charges Added (Company Currency)", - "length": 0, - "no_copy": 0, - "oldfieldname": "other_charges_added", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_taxes_and_charges_deducted", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Taxes and Charges Deducted (Company Currency)", - "length": 0, - "no_copy": 0, - "oldfieldname": "other_charges_deducted", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_total_taxes_and_charges", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Taxes and Charges (Company Currency)", - "length": 0, - "no_copy": 1, - "oldfieldname": "total_tax", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_37", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "taxes_and_charges_added", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Taxes and Charges Added", - "length": 0, - "no_copy": 0, - "oldfieldname": "other_charges_added_import", - "oldfieldtype": "Currency", - "options": "currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "taxes_and_charges_deducted", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Taxes and Charges Deducted", - "length": 0, - "no_copy": 0, - "oldfieldname": "other_charges_deducted_import", - "oldfieldtype": "Currency", - "options": "currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_taxes_and_charges", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Taxes and Charges", - "length": 0, - "no_copy": 0, - "options": "currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "discount_amount", - "columns": 0, - "fieldname": "section_break_41", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Additional Discount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Grand Total", - "fieldname": "apply_discount_on", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Apply Additional Discount On", - "length": 0, - "no_copy": 0, - "options": "\nGrand Total\nNet Total", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_discount_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Additional Discount Amount (Company Currency)", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_43", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "additional_discount_percentage", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Additional Discount Percentage", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "discount_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Additional Discount Amount", - "length": 0, - "no_copy": 0, - "options": "currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_46", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_grand_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Grand Total (Company Currency)", - "length": 0, - "no_copy": 1, - "oldfieldname": "grand_total", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_rounding_adjustment", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rounding Adjustment (Company Currency", - "length": 0, - "no_copy": 1, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "base_in_words", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "In Words (Company Currency)", - "length": 0, - "no_copy": 0, - "oldfieldname": "in_words", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_rounded_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rounded Total (Company Currency)", - "length": 0, - "no_copy": 0, - "oldfieldname": "rounded_total", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "grand_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Grand Total", - "length": 0, - "no_copy": 0, - "oldfieldname": "grand_total_import", - "oldfieldtype": "Currency", - "options": "currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "rounding_adjustment", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rounding Adjustment", - "length": 0, - "no_copy": 1, - "options": "currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "rounded_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rounded Total", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "in_words", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "In Words", - "length": 0, - "no_copy": 0, - "oldfieldname": "in_words_import", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "disable_rounded_total", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Disable Rounded Total", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "terms", - "columns": 0, - "fieldname": "terms_section_break", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Terms and Conditions", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-legal", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "tc_name", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Terms", - "length": 0, - "no_copy": 0, - "oldfieldname": "tc_name", - "oldfieldtype": "Link", - "options": "Terms and Conditions", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "terms", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Terms and Conditions", - "length": 0, - "no_copy": 0, - "oldfieldname": "terms", - "oldfieldtype": "Text Editor", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "printing_settings", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Printing Settings", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "select_print_heading", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Print Heading", - "length": 0, - "no_copy": 1, - "oldfieldname": "select_print_heading", - "oldfieldtype": "Link", - "options": "Print Heading", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "group_same_items", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Group same items", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_72", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "letter_head", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Letter Head", - "length": 0, - "no_copy": 0, - "oldfieldname": "letter_head", - "oldfieldtype": "Select", - "options": "Letter Head", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "language", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Print Language", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "subscription_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Auto Repeat Section", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "auto_repeat", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Auto Repeat", - "length": 0, - "no_copy": 1, - "options": "Auto Repeat", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: doc.auto_repeat", - "fieldname": "update_auto_repeat_reference", - "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Update Auto Repeat Reference", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "more_info", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "More Information", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-file-text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 1, - "oldfieldname": "status", - "oldfieldtype": "Select", - "options": "\nDraft\nSubmitted\nStopped\nCancelled", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_57", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "No", - "fieldname": "is_subcontracted", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Subcontracted", - "length": 0, - "no_copy": 0, - "options": "\nYes\nNo", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reference", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "opportunity", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Opportunity", - "length": 0, - "no_copy": 1, - "options": "Opportunity", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + }, + { + "fieldname": "buying_price_list", + "fieldtype": "Link", + "label": "Price List", + "options": "Price List", + "print_hide": 1 + }, + { + "depends_on": "buying_price_list", + "fieldname": "price_list_currency", + "fieldtype": "Link", + "label": "Price List Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1 + }, + { + "depends_on": "buying_price_list", + "fieldname": "plc_conversion_rate", + "fieldtype": "Float", + "label": "Price List Exchange Rate", + "precision": "9", + "print_hide": 1 + }, + { + "default": "0", + "fieldname": "ignore_pricing_rule", + "fieldtype": "Check", + "label": "Ignore Pricing Rule", + "no_copy": 1, + "permlevel": 1, + "print_hide": 1 + }, + { + "fieldname": "items_section", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break", + "options": "fa fa-shopping-cart" + }, + { + "allow_bulk_edit": 1, + "fieldname": "items", + "fieldtype": "Table", + "label": "Items", + "oldfieldname": "po_details", + "oldfieldtype": "Table", + "options": "Supplier Quotation Item", + "reqd": 1 + }, + { + "depends_on": "eval:doc.docstatus===0 && (doc.items && doc.items.length)", + "fieldname": "link_to_mrs", + "fieldtype": "Button", + "label": "Link to material requests" + }, + { + "fieldname": "pricing_rule_details", + "fieldtype": "Section Break", + "label": "Pricing Rules" + }, + { + "fieldname": "pricing_rules", + "fieldtype": "Table", + "label": "Pricing Rule Detail", + "options": "Pricing Rule Detail", + "read_only": 1 + }, + { + "fieldname": "section_break_22", + "fieldtype": "Section Break" + }, + { + "fieldname": "total_qty", + "fieldtype": "Float", + "label": "Total Quantity", + "read_only": 1 + }, + { + "fieldname": "base_total", + "fieldtype": "Currency", + "label": "Total (Company Currency)", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "base_net_total", + "fieldtype": "Currency", + "label": "Net Total (Company Currency)", + "no_copy": 1, + "oldfieldname": "net_total", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_24", + "fieldtype": "Column Break" + }, + { + "fieldname": "total", + "fieldtype": "Currency", + "label": "Total", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "net_total", + "fieldtype": "Currency", + "label": "Net Total", + "oldfieldname": "net_total_import", + "oldfieldtype": "Currency", + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "total_net_weight", + "fieldtype": "Float", + "label": "Total Net Weight", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "taxes_section", + "fieldtype": "Section Break", + "label": "Taxes and Charges", + "oldfieldtype": "Section Break", + "options": "fa fa-money" + }, + { + "fieldname": "tax_category", + "fieldtype": "Link", + "label": "Tax Category", + "options": "Tax Category", + "print_hide": 1 + }, + { + "fieldname": "column_break_36", + "fieldtype": "Column Break" + }, + { + "fieldname": "shipping_rule", + "fieldtype": "Link", + "label": "Shipping Rule", + "options": "Shipping Rule" + }, + { + "fieldname": "section_break_38", + "fieldtype": "Section Break" + }, + { + "fieldname": "taxes_and_charges", + "fieldtype": "Link", + "label": "Purchase Taxes and Charges Template", + "no_copy": 1, + "oldfieldname": "purchase_other_charges", + "oldfieldtype": "Link", + "options": "Purchase Taxes and Charges Template", + "print_hide": 1 + }, + { + "fieldname": "taxes", + "fieldtype": "Table", + "label": "Purchase Taxes and Charges", + "oldfieldname": "purchase_tax_details", + "oldfieldtype": "Table", + "options": "Purchase Taxes and Charges" + }, + { + "collapsible": 1, + "fieldname": "tax_breakup", + "fieldtype": "Section Break", + "label": "Tax Breakup" + }, + { + "fieldname": "other_charges_calculation", + "fieldtype": "Long Text", + "label": "Taxes and Charges Calculation", + "no_copy": 1, + "oldfieldtype": "HTML", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "totals", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break", + "options": "fa fa-money" + }, + { + "fieldname": "base_taxes_and_charges_added", + "fieldtype": "Currency", + "label": "Taxes and Charges Added (Company Currency)", + "oldfieldname": "other_charges_added", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "base_taxes_and_charges_deducted", + "fieldtype": "Currency", + "label": "Taxes and Charges Deducted (Company Currency)", + "oldfieldname": "other_charges_deducted", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "base_total_taxes_and_charges", + "fieldtype": "Currency", + "label": "Total Taxes and Charges (Company Currency)", + "no_copy": 1, + "oldfieldname": "total_tax", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_37", + "fieldtype": "Column Break" + }, + { + "fieldname": "taxes_and_charges_added", + "fieldtype": "Currency", + "label": "Taxes and Charges Added", + "oldfieldname": "other_charges_added_import", + "oldfieldtype": "Currency", + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "taxes_and_charges_deducted", + "fieldtype": "Currency", + "label": "Taxes and Charges Deducted", + "oldfieldname": "other_charges_deducted_import", + "oldfieldtype": "Currency", + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "total_taxes_and_charges", + "fieldtype": "Currency", + "label": "Total Taxes and Charges", + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, + { + "collapsible": 1, + "collapsible_depends_on": "discount_amount", + "fieldname": "section_break_41", + "fieldtype": "Section Break", + "label": "Additional Discount" + }, + { + "default": "Grand Total", + "fieldname": "apply_discount_on", + "fieldtype": "Select", + "label": "Apply Additional Discount On", + "options": "\nGrand Total\nNet Total", + "print_hide": 1 + }, + { + "fieldname": "base_discount_amount", + "fieldtype": "Currency", + "label": "Additional Discount Amount (Company Currency)", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break_43", + "fieldtype": "Column Break" + }, + { + "fieldname": "additional_discount_percentage", + "fieldtype": "Float", + "label": "Additional Discount Percentage", + "print_hide": 1 + }, + { + "fieldname": "discount_amount", + "fieldtype": "Currency", + "label": "Additional Discount Amount", + "options": "currency", + "print_hide": 1 + }, + { + "fieldname": "section_break_46", + "fieldtype": "Section Break" + }, + { + "fieldname": "base_grand_total", + "fieldtype": "Currency", + "label": "Grand Total (Company Currency)", + "no_copy": 1, + "oldfieldname": "grand_total", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "base_rounding_adjustment", + "fieldtype": "Currency", + "label": "Rounding Adjustment (Company Currency", + "no_copy": 1, + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "base_in_words", + "fieldtype": "Data", + "label": "In Words (Company Currency)", + "oldfieldname": "in_words", + "oldfieldtype": "Data", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "base_rounded_total", + "fieldtype": "Currency", + "label": "Rounded Total (Company Currency)", + "oldfieldname": "rounded_total", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "column_break4", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break" + }, + { + "fieldname": "grand_total", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Grand Total", + "oldfieldname": "grand_total_import", + "oldfieldtype": "Currency", + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "rounding_adjustment", + "fieldtype": "Currency", + "label": "Rounding Adjustment", + "no_copy": 1, + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "rounded_total", + "fieldtype": "Currency", + "label": "Rounded Total", + "read_only": 1 + }, + { + "fieldname": "in_words", + "fieldtype": "Data", + "label": "In Words", + "oldfieldname": "in_words_import", + "oldfieldtype": "Data", + "print_hide": 1, + "read_only": 1 + }, + { + "default": "0", + "fieldname": "disable_rounded_total", + "fieldtype": "Check", + "label": "Disable Rounded Total" + }, + { + "collapsible": 1, + "collapsible_depends_on": "terms", + "fieldname": "terms_section_break", + "fieldtype": "Section Break", + "label": "Terms and Conditions", + "oldfieldtype": "Section Break", + "options": "fa fa-legal" + }, + { + "fieldname": "tc_name", + "fieldtype": "Link", + "label": "Terms", + "oldfieldname": "tc_name", + "oldfieldtype": "Link", + "options": "Terms and Conditions", + "print_hide": 1 + }, + { + "fieldname": "terms", + "fieldtype": "Text Editor", + "label": "Terms and Conditions", + "oldfieldname": "terms", + "oldfieldtype": "Text Editor" + }, + { + "collapsible": 1, + "fieldname": "printing_settings", + "fieldtype": "Section Break", + "label": "Printing Settings" + }, + { + "allow_on_submit": 1, + "fieldname": "select_print_heading", + "fieldtype": "Link", + "label": "Print Heading", + "no_copy": 1, + "oldfieldname": "select_print_heading", + "oldfieldtype": "Link", + "options": "Print Heading", + "print_hide": 1, + "report_hide": 1 + }, + { + "allow_on_submit": 1, + "default": "0", + "fieldname": "group_same_items", + "fieldtype": "Check", + "label": "Group same items", + "print_hide": 1 + }, + { + "fieldname": "column_break_72", + "fieldtype": "Column Break" + }, + { + "allow_on_submit": 1, + "fieldname": "letter_head", + "fieldtype": "Link", + "label": "Letter Head", + "oldfieldname": "letter_head", + "oldfieldtype": "Select", + "options": "Letter Head", + "print_hide": 1 + }, + { + "fieldname": "language", + "fieldtype": "Data", + "label": "Print Language", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "subscription_section", + "fieldtype": "Section Break", + "label": "Auto Repeat Section" + }, + { + "fieldname": "auto_repeat", + "fieldtype": "Link", + "label": "Auto Repeat", + "no_copy": 1, + "options": "Auto Repeat", + "print_hide": 1, + "read_only": 1 + }, + { + "allow_on_submit": 1, + "depends_on": "eval: doc.auto_repeat", + "fieldname": "update_auto_repeat_reference", + "fieldtype": "Button", + "label": "Update Auto Repeat Reference" + }, + { + "collapsible": 1, + "fieldname": "more_info", + "fieldtype": "Section Break", + "label": "More Information", + "oldfieldtype": "Section Break", + "options": "fa fa-file-text" + }, + { + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "no_copy": 1, + "oldfieldname": "status", + "oldfieldtype": "Select", + "options": "\nDraft\nSubmitted\nStopped\nCancelled", + "print_hide": 1, + "read_only": 1, + "reqd": 1, + "search_index": 1 + }, + { + "fieldname": "column_break_57", + "fieldtype": "Column Break" + }, + { + "default": "No", + "fieldname": "is_subcontracted", + "fieldtype": "Select", + "label": "Is Subcontracted", + "options": "\nYes\nNo", + "print_hide": 1 + }, + { + "fieldname": "reference", + "fieldtype": "Section Break", + "label": "Reference" + }, + { + "fieldname": "opportunity", + "fieldtype": "Link", + "label": "Opportunity", + "no_copy": 1, + "options": "Opportunity", + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-shopping-cart", - "idx": 29, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "menu_index": 0, - "modified": "2019-02-13 00:52:28.602904", - "modified_by": "Administrator", - "module": "Buying", - "name": "Supplier Quotation", - "owner": "Administrator", + ], + "icon": "fa fa-shopping-cart", + "idx": 29, + "is_submittable": 1, + "links": [], + "modified": "2019-12-30 19:17:28.208693", + "modified_by": "Administrator", + "module": "Buying", + "name": "Supplier Quotation", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Manufacturing Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing Manager", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Purchase Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase Manager", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Purchase User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Purchase User", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Stock User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock User" + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 1, - "print": 0, - "read": 1, - "report": 0, - "role": "Purchase Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, + "permlevel": 1, + "read": 1, + "role": "Purchase Manager", "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 1, - "search_fields": "status, transaction_date, supplier,grand_total", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "timeline_field": "supplier", - "title_field": "title", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 -} + ], + "search_fields": "status, transaction_date, supplier,grand_total", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "timeline_field": "supplier", + "title_field": "title" +} \ No newline at end of file diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py index 9e201e31926..af109ba2848 100644 --- a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py +++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py @@ -138,7 +138,7 @@ def refresh_scorecards(): # Check to see if any new scorecard periods are created if make_all_scorecards(sc.name) > 0: # Save the scorecard to update the score and standings - sc.save() + frappe.get_doc('Supplier Scorecard', sc.name).save() @frappe.whitelist() diff --git a/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py index 737ddd6ddd5..87f10336f4d 100644 --- a/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py +++ b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.py @@ -43,7 +43,7 @@ class SupplierScorecardPeriod(Document): try: crit.score = min(crit.max_score, max( 0 ,frappe.safe_eval(self.get_eval_statement(crit.formula), None, {'max':max, 'min': min}))) except Exception: - frappe.throw(_("Could not solve criteria score function for {0}. Make sure the formula is valid.".format(crit.criteria_name)),frappe.ValidationError) + frappe.throw(_("Could not solve criteria score function for {0}. Make sure the formula is valid.").format(crit.criteria_name),frappe.ValidationError) crit.score = 0 def calculate_score(self): diff --git a/erpnext/buying/onboarding_slide/add_a_few_suppliers/add_a_few_suppliers.json b/erpnext/buying/onboarding_slide/add_a_few_suppliers/add_a_few_suppliers.json new file mode 100644 index 00000000000..ce3d8cfb7b2 --- /dev/null +++ b/erpnext/buying/onboarding_slide/add_a_few_suppliers/add_a_few_suppliers.json @@ -0,0 +1,49 @@ +{ + "add_more_button": 1, + "app": "ERPNext", + "creation": "2019-11-15 14:45:32.626641", + "docstatus": 0, + "doctype": "Onboarding Slide", + "domains": [], + "help_links": [ + { + "label": "Learn More", + "video_id": "zsrrVDk6VBs" + } + ], + "idx": 0, + "image_src": "", + "is_completed": 0, + "max_count": 3, + "modified": "2019-12-09 17:54:18.452038", + "modified_by": "Administrator", + "name": "Add A Few Suppliers", + "owner": "Administrator", + "ref_doctype": "Supplier", + "slide_desc": "", + "slide_fields": [ + { + "align": "", + "fieldname": "supplier_name", + "fieldtype": "Data", + "label": "Supplier Name", + "placeholder": "", + "reqd": 1 + }, + { + "align": "", + "fieldtype": "Column Break", + "reqd": 0 + }, + { + "align": "", + "fieldname": "supplier_email", + "fieldtype": "Data", + "label": "Supplier Email", + "reqd": 1 + } + ], + "slide_order": 50, + "slide_title": "Add A Few Suppliers", + "slide_type": "Create" +} \ No newline at end of file diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.py b/erpnext/buying/report/procurement_tracker/procurement_tracker.py index 48295bee26a..866bf0c7332 100644 --- a/erpnext/buying/report/procurement_tracker/procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.py @@ -141,13 +141,13 @@ def get_conditions(filters): conditions = "" if filters.get("company"): - conditions += " AND company='%s'"% filters.get('company') + conditions += " AND company=%s"% frappe.db.escape(filters.get('company')) if filters.get("cost_center") or filters.get("project"): conditions += """ - AND (cost_center='%s' - OR project='%s') - """% (filters.get('cost_center'), filters.get('project')) + AND (cost_center=%s + OR project=%s) + """% (frappe.db.escape(filters.get('cost_center')), frappe.db.escape(filters.get('project'))) if filters.get("from_date"): conditions += " AND transaction_date>=%s"% filters.get('from_date') diff --git a/erpnext/buying/utils.py b/erpnext/buying/utils.py index 8c0a1e56f7d..b5598f8d0b2 100644 --- a/erpnext/buying/utils.py +++ b/erpnext/buying/utils.py @@ -24,12 +24,12 @@ def update_last_purchase_rate(doc, is_submit): last_purchase_rate = None if last_purchase_details and \ (last_purchase_details.purchase_date > this_purchase_date): - last_purchase_rate = last_purchase_details['base_rate'] + last_purchase_rate = last_purchase_details['base_net_rate'] elif is_submit == 1: # even if this transaction is the latest one, it should be submitted # for it to be considered for latest purchase rate if flt(d.conversion_factor): - last_purchase_rate = flt(d.base_rate) / flt(d.conversion_factor) + last_purchase_rate = flt(d.base_net_rate) / flt(d.conversion_factor) # Check if item code is present # Conversion factor should not be mandatory for non itemized items elif d.item_code: diff --git a/erpnext/change_log/v12/v12_2_0.md b/erpnext/change_log/v12/v12_2_0.md new file mode 100644 index 00000000000..0ec0eeca3df --- /dev/null +++ b/erpnext/change_log/v12/v12_2_0.md @@ -0,0 +1,14 @@ +# Version 12.2.0 Release Notes + +### Accounting + +1. Fixed Asset + - "Enable CWIP" options moved to Asset Category from Asset Settings + - Removed Asset link from Purchase Receipt Item table + - Enhanced Asset master + - Asset Movement now handles movement of multiple assets + - Introduced monthly depreciation +2. GL Entries for Landed Cost Voucher now posted directly against individual Charges account +3. Optimization of BOM Update Tool +4. Syncing of Stock and Account balance is enforced, in case of perpetual inventory +5. Rendered email template in Email Campaign diff --git a/erpnext/config/accounts.py b/erpnext/config/accounts.py index ab75f211c04..08711fc09ea 100644 --- a/erpnext/config/accounts.py +++ b/erpnext/config/accounts.py @@ -197,6 +197,11 @@ def get_data(): "name": "Bank Reconciliation Statement", "is_query_report": True, "doctype": "Journal Entry" + },{ + "type": "page", + "name": "bank-reconciliation", + "label": _("Bank Reconciliation"), + "icon": "fa fa-bar-chart" }, { "type": "report", diff --git a/erpnext/config/assets.py b/erpnext/config/assets.py index 3c9452f5a4f..4cf7cf08067 100644 --- a/erpnext/config/assets.py +++ b/erpnext/config/assets.py @@ -21,10 +21,6 @@ def get_data(): "name": "Asset Category", "onboard": 1, }, - { - "type": "doctype", - "name": "Asset Settings", - }, { "type": "doctype", "name": "Asset Movement", diff --git a/erpnext/config/crm.py b/erpnext/config/crm.py index eba6c7a02a5..09c2a65633b 100644 --- a/erpnext/config/crm.py +++ b/erpnext/config/crm.py @@ -46,6 +46,16 @@ def get_data(): "name": "Contract", "description": _("Helps you keep tracks of Contracts based on Supplier, Customer and Employee"), }, + { + "type": "doctype", + "name": "Appointment", + "description" : _("Helps you manage appointments with your leads"), + }, + { + "type": "doctype", + "name": "Newsletter", + "label": _("Newsletter"), + } ] }, { @@ -107,6 +117,13 @@ def get_data(): "name": "Lead Owner Efficiency", "doctype": "Lead", "dependencies": ["Lead"] + }, + { + "type": "report", + "is_query_report": True, + "name": "Territory-wise Sales", + "doctype": "Opportunity", + "dependencies": ["Opportunity"] } ] }, @@ -165,6 +182,11 @@ def get_data(): "type": "doctype", "name": "SMS Settings", "description": _("Setup SMS gateway settings") + }, + { + "type": "doctype", + "label": _("Email Group"), + "name": "Email Group", } ] }, diff --git a/erpnext/config/hr.py b/erpnext/config/hr.py index 4e5e9037b3a..7b3b4660f57 100644 --- a/erpnext/config/hr.py +++ b/erpnext/config/hr.py @@ -289,6 +289,10 @@ def get_data(): "name": "Job Offer", "onboard": 1, }, + { + "type": "doctype", + "name": "Appointment Letter", + }, { "type": "doctype", "name": "Staffing Plan", diff --git a/erpnext/config/stock.py b/erpnext/config/stock.py index 441a3ab4ec3..dd35f5ab368 100644 --- a/erpnext/config/stock.py +++ b/erpnext/config/stock.py @@ -241,6 +241,10 @@ def get_data(): "type": "doctype", "name": "Quality Inspection Template", }, + { + "type": "doctype", + "name": "Quick Stock Balance", + }, ] }, { @@ -332,6 +336,24 @@ def get_data(): "is_query_report": True, "name": "Item Variant Details", "doctype": "Item" + }, + { + "type": "report", + "is_query_report": True, + "name": "Subcontracted Raw Materials To Be Transferred", + "doctype": "Purchase Order" + }, + { + "type": "report", + "is_query_report": True, + "name": "Subcontracted Item To Be Received", + "doctype": "Purchase Order" + }, + { + "type": "report", + "is_query_report": True, + "name": "Stock and Account Value Comparison", + "doctype": "Stock Ledger Entry" } ] }, diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 94152284670..56a09c356d6 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -5,15 +5,17 @@ from __future__ import unicode_literals import frappe, erpnext import json from frappe import _, throw -from frappe.utils import today, flt, cint, fmt_money, formatdate, getdate, add_days, add_months, get_last_day, nowdate -from erpnext.stock.get_item_details import get_conversion_factor +from frappe.utils import (today, flt, cint, fmt_money, formatdate, + getdate, add_days, add_months, get_last_day, nowdate, get_link_to_form) +from erpnext.stock.get_item_details import get_conversion_factor, get_item_details from erpnext.setup.utils import get_exchange_rate from erpnext.accounts.utils import get_fiscal_years, validate_fiscal_year, get_account_currency from erpnext.utilities.transaction_base import TransactionBase from erpnext.buying.utils import update_last_purchase_rate from erpnext.controllers.sales_and_purchase_return import validate_return from erpnext.accounts.party import get_party_account_currency, validate_party_frozen_disabled -from erpnext.accounts.doctype.pricing_rule.utils import validate_pricing_rules +from erpnext.accounts.doctype.pricing_rule.utils import (apply_pricing_rule_on_transaction, + apply_pricing_rule_for_free_items, get_applied_pricing_rules) from erpnext.exceptions import InvalidCurrency from six import text_type from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions @@ -56,10 +58,9 @@ class AccountsController(TransactionBase): (is_supplier_payment and supplier.hold_type in ['All', 'Payments']): if not supplier.release_date or getdate(nowdate()) <= supplier.release_date: frappe.msgprint( - _('{0} is blocked so this transaction cannot proceed'.format(supplier_name)), raise_exception=1) + _('{0} is blocked so this transaction cannot proceed').format(supplier_name), raise_exception=1) def validate(self): - if not self.get('is_return'): self.validate_qty_is_not_zero() @@ -98,10 +99,22 @@ class AccountsController(TransactionBase): if self.is_return: self.validate_qty() + else: + self.validate_deferred_start_and_end_date() validate_regional(self) if self.doctype != 'Material Request': - validate_pricing_rules(self) + apply_pricing_rule_on_transaction(self) + + def validate_deferred_start_and_end_date(self): + for d in self.items: + if d.get("enable_deferred_revenue") or d.get("enable_deferred_expense"): + if not (d.service_start_date and d.service_end_date): + frappe.throw(_("Row #{0}: Service Start and End Date is required for deferred accounting").format(d.idx)) + elif getdate(d.service_start_date) > getdate(d.service_end_date): + frappe.throw(_("Row #{0}: Service Start Date cannot be greater than Service End Date").format(d.idx)) + elif getdate(self.posting_date) > getdate(d.service_end_date): + frappe.throw(_("Row #{0}: Service End Date cannot be before Invoice Posting Date").format(d.idx)) def validate_invoice_documents_schedule(self): self.validate_payment_schedule_dates() @@ -232,7 +245,6 @@ class AccountsController(TransactionBase): def set_missing_item_details(self, for_validate=False): """set missing item values""" - from erpnext.stock.get_item_details import get_item_details from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos if hasattr(self, "items"): @@ -244,7 +256,6 @@ class AccountsController(TransactionBase): document_type = "{} Item".format(self.doctype) parent_dict.update({"document_type": document_type}) - self.set('pricing_rules', []) # party_name field used for customer in quotation if self.doctype == "Quotation" and self.quotation_to == "Customer" and parent_dict.get("party_name"): parent_dict.update({"customer": parent_dict.get("party_name")}) @@ -264,7 +275,7 @@ class AccountsController(TransactionBase): if self.get("is_subcontracted"): args["is_subcontracted"] = self.is_subcontracted - ret = get_item_details(args, self, overwrite_warehouse=False) + ret = get_item_details(args, self, for_validate=True, overwrite_warehouse=False) for fieldname, value in ret.items(): if item.meta.get_field(fieldname) and value is not None: @@ -285,24 +296,42 @@ class AccountsController(TransactionBase): if self.doctype in ["Purchase Invoice", "Sales Invoice"] and item.meta.get_field('is_fixed_asset'): item.set('is_fixed_asset', ret.get('is_fixed_asset', 0)) - if ret.get("pricing_rules") and not ret.get("validate_applied_rule", 0): - # if user changed the discount percentage then set user's discount percentage ? - item.set("pricing_rules", ret.get("pricing_rules")) - item.set("discount_percentage", ret.get("discount_percentage")) - item.set("discount_amount", ret.get("discount_amount")) - if ret.get("pricing_rule_for") == "Rate": - item.set("price_list_rate", ret.get("price_list_rate")) - - if item.get("price_list_rate"): - item.rate = flt(item.price_list_rate * - (1.0 - (flt(item.discount_percentage) / 100.0)), item.precision("rate")) - - if item.get('discount_amount'): - item.rate = item.price_list_rate - item.discount_amount + if ret.get("pricing_rules"): + self.apply_pricing_rule_on_items(item, ret) if self.doctype == "Purchase Invoice": self.set_expense_account(for_validate) + def apply_pricing_rule_on_items(self, item, pricing_rule_args): + if not pricing_rule_args.get("validate_applied_rule", 0): + # if user changed the discount percentage then set user's discount percentage ? + if pricing_rule_args.get("price_or_product_discount") == 'Price': + item.set("pricing_rules", pricing_rule_args.get("pricing_rules")) + item.set("discount_percentage", pricing_rule_args.get("discount_percentage")) + item.set("discount_amount", pricing_rule_args.get("discount_amount")) + if pricing_rule_args.get("pricing_rule_for") == "Rate": + item.set("price_list_rate", pricing_rule_args.get("price_list_rate")) + + if item.get("price_list_rate"): + item.rate = flt(item.price_list_rate * + (1.0 - (flt(item.discount_percentage) / 100.0)), item.precision("rate")) + + if item.get('discount_amount'): + item.rate = item.price_list_rate - item.discount_amount + + elif pricing_rule_args.get('free_item_data'): + apply_pricing_rule_for_free_items(self, pricing_rule_args.get('free_item_data')) + + elif pricing_rule_args.get("validate_applied_rule"): + for pricing_rule in get_applied_pricing_rules(item): + pricing_rule_doc = frappe.get_cached_doc("Pricing Rule", pricing_rule) + for field in ['discount_percentage', 'discount_amount', 'rate']: + if item.get(field) < pricing_rule_doc.get(field): + title = get_link_to_form("Pricing Rule", pricing_rule) + + frappe.msgprint(_("Row {0}: user has not applied the rule {1} on the item {2}") + .format(item.idx, frappe.bold(title), frappe.bold(item.item_code))) + def set_taxes(self): if not self.meta.get_field("taxes"): return @@ -397,9 +426,10 @@ class AccountsController(TransactionBase): return gl_dict def validate_qty_is_not_zero(self): - for item in self.items: - if not item.qty: - frappe.throw(_("Item quantity can not be zero")) + if self.doctype != "Purchase Receipt": + for item in self.items: + if not item.qty: + frappe.throw(_("Item quantity can not be zero")) def validate_account_currency(self, account, account_currency=None): valid_currency = [self.company_currency] @@ -718,48 +748,6 @@ class AccountsController(TransactionBase): # at quotation / sales order level and we shouldn't stop someone # from creating a sales invoice if sales order is already created - def validate_fixed_asset(self): - for d in self.get("items"): - if d.is_fixed_asset: - # if d.qty > 1: - # frappe.throw(_("Row #{0}: Qty must be 1, as item is a fixed asset. Please use separate row for multiple qty.").format(d.idx)) - - if d.meta.get_field("asset") and d.asset: - asset = frappe.get_doc("Asset", d.asset) - - if asset.company != self.company: - frappe.throw(_("Row #{0}: Asset {1} does not belong to company {2}") - .format(d.idx, d.asset, self.company)) - - elif asset.item_code != d.item_code: - frappe.throw(_("Row #{0}: Asset {1} does not linked to Item {2}") - .format(d.idx, d.asset, d.item_code)) - - # elif asset.docstatus != 1: - # frappe.throw(_("Row #{0}: Asset {1} must be submitted").format(d.idx, d.asset)) - - elif self.doctype == "Purchase Invoice": - # if asset.status != "Submitted": - # frappe.throw(_("Row #{0}: Asset {1} is already {2}") - # .format(d.idx, d.asset, asset.status)) - if getdate(asset.purchase_date) != getdate(self.posting_date): - frappe.throw( - _("Row #{0}: Posting Date must be same as purchase date {1} of asset {2}").format(d.idx, - asset.purchase_date, - d.asset)) - elif asset.is_existing_asset: - frappe.throw( - _("Row #{0}: Purchase Invoice cannot be made against an existing asset {1}").format( - d.idx, d.asset)) - - elif self.docstatus == "Sales Invoice" and self.docstatus == 1: - if self.update_stock: - frappe.throw(_("'Update Stock' cannot be checked for fixed asset sale")) - - elif asset.status in ("Scrapped", "Cancelled", "Sold"): - frappe.throw(_("Row #{0}: Asset {1} cannot be submitted, it is already {2}") - .format(d.idx, d.asset, asset.status)) - def delink_advance_entries(self, linked_doc_name): total_allocated_amount = 0 for adv in self.advances: @@ -938,7 +926,7 @@ def validate_taxes_and_charges(tax): frappe.throw( _("Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row")) elif not tax.row_id: - frappe.throw(_("Please specify a valid Row ID for row {0} in table {1}".format(tax.idx, _(tax.doctype)))) + frappe.throw(_("Please specify a valid Row ID for row {0} in table {1}").format(tax.idx, _(tax.doctype))) elif tax.row_id and cint(tax.row_id) >= cint(tax.idx): frappe.throw(_("Cannot refer row number greater than or equal to current row number for this Charge type")) @@ -1167,6 +1155,30 @@ def set_purchase_order_defaults(parent_doctype, parent_doctype_name, child_docna child_item.base_amount = 1 # Initiallize value will update in parent validation return child_item +def check_and_delete_children(parent, data): + deleted_children = [] + updated_item_names = [d.get("docname") for d in data] + for item in parent.items: + if item.name not in updated_item_names: + deleted_children.append(item) + + for d in deleted_children: + if parent.doctype == "Sales Order": + if flt(d.delivered_qty): + frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been delivered").format(d.idx, d.item_code)) + if flt(d.work_order_qty): + frappe.throw(_("Row #{0}: Cannot delete item {1} which has work order assigned to it.").format(d.idx, d.item_code)) + if flt(d.ordered_qty): + frappe.throw(_("Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.").format(d.idx, d.item_code)) + + if parent.doctype == "Purchase Order" and flt(d.received_qty): + frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been received").format(d.idx, d.item_code)) + + if flt(d.billed_amt): + frappe.throw(_("Row #{0}: Cannot delete item {1} which has already been billed.").format(d.idx, d.item_code)) + + d.cancel() + d.delete() @frappe.whitelist() def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"): @@ -1175,6 +1187,8 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation'] parent = frappe.get_doc(parent_doctype, parent_doctype_name) + check_and_delete_children(parent, data) + for d in data: new_child_flag = False if not d.get("docname"): diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 0dde8980051..69caabd7249 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -101,7 +101,7 @@ class BuyingController(StockController): msgprint(_('Tax Category has been changed to "Total" because all the Items are non-stock items')) def get_asset_items(self): - if self.doctype not in ['Purchase Invoice', 'Purchase Receipt']: + if self.doctype not in ['Purchase Order', 'Purchase Invoice', 'Purchase Receipt']: return [] return [d.item_code for d in self.items if d.is_fixed_asset] @@ -150,25 +150,26 @@ class BuyingController(StockController): TODO: rename item_tax_amount to valuation_tax_amount """ - stock_items = self.get_stock_items() + self.get_asset_items() + stock_and_asset_items = self.get_stock_items() + self.get_asset_items() - stock_items_qty, stock_items_amount = 0, 0 - last_stock_item_idx = 1 + stock_and_asset_items_qty, stock_and_asset_items_amount = 0, 0 + last_item_idx = 1 for d in self.get(parentfield): - if d.item_code and d.item_code in stock_items: - stock_items_qty += flt(d.qty) - stock_items_amount += flt(d.base_net_amount) - last_stock_item_idx = d.idx + if d.item_code and d.item_code in stock_and_asset_items: + stock_and_asset_items_qty += flt(d.qty) + stock_and_asset_items_amount += flt(d.base_net_amount) + last_item_idx = d.idx total_valuation_amount = sum([flt(d.base_tax_amount_after_discount_amount) for d in self.get("taxes") if d.category in ["Valuation", "Valuation and Total"]]) valuation_amount_adjustment = total_valuation_amount for i, item in enumerate(self.get(parentfield)): - if item.item_code and item.qty and item.item_code in stock_items: - item_proportion = flt(item.base_net_amount) / stock_items_amount if stock_items_amount \ - else flt(item.qty) / stock_items_qty - if i == (last_stock_item_idx - 1): + if item.item_code and item.qty and item.item_code in stock_and_asset_items: + item_proportion = flt(item.base_net_amount) / stock_and_asset_items_amount if stock_and_asset_items_amount \ + else flt(item.qty) / stock_and_asset_items_qty + + if i == (last_item_idx - 1): item.item_tax_amount = flt(valuation_amount_adjustment, self.precision("item_tax_amount", item)) else: @@ -220,7 +221,7 @@ class BuyingController(StockController): "backflush_raw_materials_of_subcontract_based_on") if (self.doctype == 'Purchase Receipt' and backflush_raw_materials_based_on != 'BOM'): - self.update_raw_materials_supplied_based_on_stock_entries(raw_material_table) + self.update_raw_materials_supplied_based_on_stock_entries() else: for item in self.get("items"): if self.doctype in ["Purchase Receipt", "Purchase Invoice"]: @@ -240,41 +241,96 @@ class BuyingController(StockController): if self.is_subcontracted == "No" and self.get("supplied_items"): self.set('supplied_items', []) - def update_raw_materials_supplied_based_on_stock_entries(self, raw_material_table): - self.set(raw_material_table, []) - purchase_orders = [d.purchase_order for d in self.items] - if purchase_orders: - items = get_subcontracted_raw_materials_from_se(purchase_orders) - backflushed_raw_materials = get_backflushed_subcontracted_raw_materials_from_se(purchase_orders, self.name) + def update_raw_materials_supplied_based_on_stock_entries(self): + self.set('supplied_items', []) - for d in items: - qty = d.qty - backflushed_raw_materials.get(d.item_code, 0) - rm = self.append(raw_material_table, {}) - rm.rm_item_code = d.item_code - rm.item_name = d.item_name - rm.main_item_code = d.main_item_code - rm.description = d.description - rm.stock_uom = d.stock_uom - rm.required_qty = qty - rm.consumed_qty = qty - rm.serial_no = d.serial_no - rm.batch_no = d.batch_no + purchase_orders = set([d.purchase_order for d in self.items]) - # get raw materials rate - from erpnext.stock.utils import get_incoming_rate - rm.rate = get_incoming_rate({ - "item_code": d.item_code, - "warehouse": self.supplier_warehouse, - "posting_date": self.posting_date, - "posting_time": self.posting_time, - "qty": -1 * qty, - "serial_no": rm.serial_no - }) - if not rm.rate: - rm.rate = get_valuation_rate(d.item_code, self.supplier_warehouse, - self.doctype, self.name, currency=self.company_currency, company = self.company) + # qty of raw materials backflushed (for each item per purchase order) + backflushed_raw_materials_map = get_backflushed_subcontracted_raw_materials(purchase_orders) - rm.amount = qty * flt(rm.rate) + # qty of "finished good" item yet to be received + qty_to_be_received_map = get_qty_to_be_received(purchase_orders) + + for item in self.get('items'): + # reset raw_material cost + item.rm_supp_cost = 0 + + # qty of raw materials transferred to the supplier + transferred_raw_materials = get_subcontracted_raw_materials_from_se(item.purchase_order, item.item_code) + + non_stock_items = get_non_stock_items(item.purchase_order, item.item_code) + + item_key = '{}{}'.format(item.item_code, item.purchase_order) + + fg_yet_to_be_received = qty_to_be_received_map.get(item_key) + + transferred_batch_qty_map = get_transferred_batch_qty_map(item.purchase_order, item.item_code) + backflushed_batch_qty_map = get_backflushed_batch_qty_map(item.purchase_order, item.item_code) + + for raw_material in transferred_raw_materials + non_stock_items: + rm_item_key = '{}{}'.format(raw_material.rm_item_code, item.purchase_order) + raw_material_data = backflushed_raw_materials_map.get(rm_item_key, {}) + + consumed_qty = raw_material_data.get('qty', 0) + consumed_serial_nos = raw_material_data.get('serial_nos', '') + consumed_batch_nos = raw_material_data.get('batch_nos', '') + + transferred_qty = raw_material.qty + + rm_qty_to_be_consumed = transferred_qty - consumed_qty + + # backflush all remaining transferred qty in the last Purchase Receipt + if fg_yet_to_be_received == item.qty: + qty = rm_qty_to_be_consumed + else: + qty = (rm_qty_to_be_consumed / fg_yet_to_be_received) * item.qty + + if frappe.get_cached_value('UOM', raw_material.stock_uom, 'must_be_whole_number'): + qty = frappe.utils.ceil(qty) + + if qty > rm_qty_to_be_consumed: + qty = rm_qty_to_be_consumed + + if not qty: continue + + if raw_material.serial_nos: + set_serial_nos(raw_material, consumed_serial_nos, qty) + + if raw_material.batch_nos: + batches_qty = get_batches_with_qty(raw_material.rm_item_code, raw_material.main_item_code, + qty, transferred_batch_qty_map, backflushed_batch_qty_map) + for batch_data in batches_qty: + qty = batch_data['qty'] + raw_material.batch_no = batch_data['batch'] + self.append_raw_material_to_be_backflushed(item, raw_material, qty) + else: + self.append_raw_material_to_be_backflushed(item, raw_material, qty) + + def append_raw_material_to_be_backflushed(self, fg_item_doc, raw_material_data, qty): + rm = self.append('supplied_items', {}) + rm.update(raw_material_data) + + rm.required_qty = qty + rm.consumed_qty = qty + + if not raw_material_data.get('non_stock_item'): + from erpnext.stock.utils import get_incoming_rate + rm.rate = get_incoming_rate({ + "item_code": raw_material_data.rm_item_code, + "warehouse": self.supplier_warehouse, + "posting_date": self.posting_date, + "posting_time": self.posting_time, + "qty": -1 * qty, + "serial_no": rm.serial_no + }) + + if not rm.rate: + rm.rate = get_valuation_rate(raw_material_data.item_code, self.supplier_warehouse, + self.doctype, self.name, currency=self.company_currency, company=self.company) + + rm.amount = qty * flt(rm.rate) + fg_item_doc.rm_supp_cost += rm.amount def update_raw_materials_supplied_based_on_bom(self, item, raw_material_table): exploded_item = 1 @@ -386,9 +442,11 @@ class BuyingController(StockController): item_codes = list(set(item.item_code for item in self.get("items"))) if item_codes: - self._sub_contracted_items = [r[0] for r in frappe.db.sql("""select name - from `tabItem` where name in (%s) and is_sub_contracted_item=1""" % \ - (", ".join((["%s"]*len(item_codes))),), item_codes)] + items = frappe.get_all('Item', filters={ + 'name': ['in', item_codes], + 'is_sub_contracted_item': 1 + }) + self._sub_contracted_items = [item.name for item in items] return self._sub_contracted_items @@ -442,8 +500,8 @@ class BuyingController(StockController): item_row = item_row.as_dict() for fieldname in field_list: if flt(item_row[fieldname]) < 0: - frappe.throw(_("Row #{0}: {1} can not be negative for item {2}".format(item_row['idx'], - frappe.get_meta(item_row.doctype).get_label(fieldname), item_row['item_code']))) + frappe.throw(_("Row #{0}: {1} can not be negative for item {2}").format(item_row['idx'], + frappe.get_meta(item_row.doctype).get_label(fieldname), item_row['item_code'])) def check_for_on_hold_or_closed_status(self, ref_doctype, ref_fieldname): for d in self.get("items"): @@ -572,43 +630,33 @@ class BuyingController(StockController): asset_items = self.get_asset_items() if asset_items: - self.make_serial_nos_for_asset(asset_items) + self.auto_make_assets(asset_items) - def make_serial_nos_for_asset(self, asset_items): + def auto_make_assets(self, asset_items): items_data = get_asset_item_details(asset_items) + messages = [] for d in self.items: if d.is_fixed_asset: item_data = items_data.get(d.item_code) - if not d.asset: - asset = self.make_asset(d) - d.db_set('asset', asset) - if item_data.get('has_serial_no'): - # If item has serial no - if item_data.get('serial_no_series') and not d.serial_no: - serial_nos = get_auto_serial_nos(item_data.get('serial_no_series'), d.qty) - elif d.serial_no: - serial_nos = d.serial_no - elif not d.serial_no: - frappe.throw(_("Serial no is mandatory for the item {0}").format(d.item_code)) + if item_data.get('auto_create_assets'): + # If asset has to be auto created + # Check for asset naming series + if item_data.get('asset_naming_series'): + for qty in range(cint(d.qty)): + self.make_asset(d) + is_plural = 's' if cint(d.qty) != 1 else '' + messages.append(_('{0} Asset{2} Created for {1}').format(cint(d.qty), d.item_code, is_plural)) + else: + frappe.throw(_("Row {1}: Asset Naming Series is mandatory for the auto creation for item {0}") + .format(d.item_code, d.idx)) + else: + messages.append(_("Assets not created for {0}. You will have to create asset manually.") + .format(d.item_code)) - auto_make_serial_nos({ - 'serial_no': serial_nos, - 'item_code': d.item_code, - 'via_stock_ledger': False, - 'company': self.company, - 'supplier': self.supplier, - 'actual_qty': d.qty, - 'purchase_document_type': self.doctype, - 'purchase_document_no': self.name, - 'asset': d.asset, - 'location': d.asset_location - }) - d.db_set('serial_no', serial_nos) - - if d.asset: - self.make_asset_movement(d) + for message in messages: + frappe.msgprint(message, title="Success") def make_asset(self, row): if not row.asset_location: @@ -617,7 +665,7 @@ class BuyingController(StockController): item_data = frappe.db.get_value('Item', row.item_code, ['asset_naming_series', 'asset_category'], as_dict=1) - purchase_amount = flt(row.base_net_amount + row.item_tax_amount) + purchase_amount = flt(row.base_rate + row.item_tax_amount) asset = frappe.get_doc({ 'doctype': 'Asset', 'item_code': row.item_code, @@ -640,57 +688,49 @@ class BuyingController(StockController): asset.set_missing_values() asset.insert() - asset_link = frappe.utils.get_link_to_form('Asset', asset.name) - frappe.msgprint(_("Asset {0} created").format(asset_link)) - return asset.name - - def make_asset_movement(self, row): - asset_movement = frappe.get_doc({ - 'doctype': 'Asset Movement', - 'asset': row.asset, - 'target_location': row.asset_location, - 'purpose': 'Receipt', - 'serial_no': row.serial_no, - 'quantity': len(get_serial_nos(row.serial_no)), - 'company': self.company, - 'transaction_date': self.posting_date, - 'reference_doctype': self.doctype, - 'reference_name': self.name - }).insert() - - return asset_movement.name - def update_fixed_asset(self, field, delete_asset = False): for d in self.get("items"): - if d.is_fixed_asset and d.asset: - asset = frappe.get_doc("Asset", d.asset) + if d.is_fixed_asset: + is_auto_create_enabled = frappe.db.get_value('Item', d.item_code, 'auto_create_assets') + assets = frappe.db.get_all('Asset', filters={ field : self.name, 'item_code' : d.item_code }) - if delete_asset and asset.docstatus == 0: - frappe.delete_doc("Asset", asset.name) - d.db_set('asset', None) - continue + for asset in assets: + asset = frappe.get_doc('Asset', asset.name) + if delete_asset and is_auto_create_enabled: + # need to delete movements to delete assets otherwise throws link exists error + movements = frappe.db.sql( + """SELECT asm.name + FROM `tabAsset Movement` asm, `tabAsset Movement Item` asm_item + WHERE asm_item.parent=asm.name and asm_item.asset=%s""", asset.name, as_dict=1) + for movement in movements: + frappe.delete_doc('Asset Movement', movement.name, force=1) + frappe.delete_doc("Asset", asset.name, force=1) + continue - if self.docstatus in [0, 1] and not asset.get(field): - asset.set(field, self.name) - asset.purchase_date = self.posting_date - asset.supplier = self.supplier - elif self.docstatus == 2: - asset.set(field, None) - asset.supplier = None + if self.docstatus in [0, 1] and not asset.get(field): + asset.set(field, self.name) + asset.purchase_date = self.posting_date + asset.supplier = self.supplier + elif self.docstatus == 2: + if asset.docstatus == 0: + asset.set(field, None) + asset.supplier = None + if asset.docstatus == 1 and delete_asset: + frappe.throw(_('Cannot cancel this document as it is linked with submitted asset {0}.\ + Please cancel the it to continue.').format(asset.name)) - asset.flags.ignore_validate_update_after_submit = True - asset.flags.ignore_mandatory = True - if asset.docstatus == 0: - asset.flags.ignore_validate = True + asset.flags.ignore_validate_update_after_submit = True + asset.flags.ignore_mandatory = True + if asset.docstatus == 0: + asset.flags.ignore_validate = True - asset.save() + asset.save() def delete_linked_asset(self): if self.doctype == 'Purchase Invoice' and not self.get('update_stock'): return frappe.db.sql("delete from `tabAsset Movement` where reference_name=%s", self.name) - frappe.db.sql("delete from `tabSerial No` where purchase_document_no=%s", self.name) def validate_schedule_date(self): if not self.get("items"): @@ -739,32 +779,76 @@ def get_items_from_bom(item_code, bom, exploded_item=1): return bom_items -def get_subcontracted_raw_materials_from_se(purchase_orders): - return frappe.db.sql(""" - select - sed.item_name, sed.item_code, sum(sed.qty) as qty, sed.description, - sed.stock_uom, sed.subcontracted_item as main_item_code, sed.serial_no, sed.batch_no - from `tabStock Entry` se,`tabStock Entry Detail` sed - where - se.name = sed.parent and se.docstatus=1 and se.purpose='Send to Subcontractor' - and se.purchase_order in (%s) and ifnull(sed.t_warehouse, '') != '' - group by sed.item_code, sed.t_warehouse - """ % (','.join(['%s'] * len(purchase_orders))), tuple(purchase_orders), as_dict=1) +def get_subcontracted_raw_materials_from_se(purchase_order, fg_item): + common_query = """ + SELECT + sed.item_code AS rm_item_code, + SUM(sed.qty) AS qty, + sed.description, + sed.stock_uom, + sed.subcontracted_item AS main_item_code, + {serial_no_concat_syntax} AS serial_nos, + {batch_no_concat_syntax} AS batch_nos + FROM `tabStock Entry` se,`tabStock Entry Detail` sed + WHERE + se.name = sed.parent + AND se.docstatus=1 + AND se.purpose='Send to Subcontractor' + AND se.purchase_order = %s + AND IFNULL(sed.t_warehouse, '') != '' + AND sed.subcontracted_item = %s + GROUP BY sed.item_code, sed.subcontracted_item + """ + raw_materials = frappe.db.multisql({ + 'mariadb': common_query.format( + serial_no_concat_syntax="GROUP_CONCAT(sed.serial_no)", + batch_no_concat_syntax="GROUP_CONCAT(sed.batch_no)" + ), + 'postgres': common_query.format( + serial_no_concat_syntax="STRING_AGG(sed.serial_no, ',')", + batch_no_concat_syntax="STRING_AGG(sed.batch_no, ',')" + ) + }, (purchase_order, fg_item), as_dict=1) -def get_backflushed_subcontracted_raw_materials_from_se(purchase_orders, purchase_receipt): - return frappe._dict(frappe.db.sql(""" - select - prsi.rm_item_code as item_code, sum(prsi.consumed_qty) as qty - from `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pri, `tabPurchase Receipt Item Supplied` prsi - where - pr.name = pri.parent and pr.name = prsi.parent and pri.purchase_order in (%s) - and pri.item_code = prsi.main_item_code and pr.name != '%s' and pr.docstatus = 1 - group by prsi.rm_item_code - """ % (','.join(['%s'] * len(purchase_orders)), purchase_receipt), tuple(purchase_orders))) + return raw_materials + +def get_backflushed_subcontracted_raw_materials(purchase_orders): + common_query = """ + SELECT + CONCAT(prsi.rm_item_code, pri.purchase_order) AS item_key, + SUM(prsi.consumed_qty) AS qty, + {serial_no_concat_syntax} AS serial_nos, + {batch_no_concat_syntax} AS batch_nos + FROM `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pri, `tabPurchase Receipt Item Supplied` prsi + WHERE + pr.name = pri.parent + AND pr.name = prsi.parent + AND pri.purchase_order IN %s + AND pri.item_code = prsi.main_item_code + AND pr.docstatus = 1 + GROUP BY prsi.rm_item_code, pri.purchase_order + """ + + backflushed_raw_materials = frappe.db.multisql({ + 'mariadb': common_query.format( + serial_no_concat_syntax="GROUP_CONCAT(prsi.serial_no)", + batch_no_concat_syntax="GROUP_CONCAT(prsi.batch_no)" + ), + 'postgres': common_query.format( + serial_no_concat_syntax="STRING_AGG(prsi.serial_no, ',')", + batch_no_concat_syntax="STRING_AGG(prsi.batch_no, ',')" + ) + }, (purchase_orders, ), as_dict=1) + + backflushed_raw_materials_map = frappe._dict() + for item in backflushed_raw_materials: + backflushed_raw_materials_map.setdefault(item.item_key, item) + + return backflushed_raw_materials_map def get_asset_item_details(asset_items): asset_items_data = {} - for d in frappe.get_all('Item', fields = ["name", "has_serial_no", "serial_no_series"], + for d in frappe.get_all('Item', fields = ["name", "auto_create_assets", "asset_naming_series"], filters = {'name': ('in', asset_items)}): asset_items_data.setdefault(d.name, d) @@ -788,8 +872,130 @@ def validate_item_type(doc, fieldname, message): items = ", ".join([d for d in invalid_items]) if len(invalid_items) > 1: - error_message = _("Following items {0} are not marked as {1} item. You can enable them as {1} item from its Item master".format(items, message)) + error_message = _("Following items {0} are not marked as {1} item. You can enable them as {1} item from its Item master").format(items, message) else: - error_message = _("Following item {0} is not marked as {1} item. You can enable them as {1} item from its Item master".format(items, message)) + error_message = _("Following item {0} is not marked as {1} item. You can enable them as {1} item from its Item master").format(items, message) frappe.throw(error_message) + +def get_qty_to_be_received(purchase_orders): + return frappe._dict(frappe.db.sql(""" + SELECT CONCAT(poi.`item_code`, poi.`parent`) AS item_key, + SUM(poi.`qty`) - SUM(poi.`received_qty`) AS qty_to_be_received + FROM `tabPurchase Order Item` poi + WHERE + poi.`parent` in %s + GROUP BY poi.`item_code`, poi.`parent` + HAVING SUM(poi.`qty`) > SUM(poi.`received_qty`) + """, (purchase_orders))) + +def get_non_stock_items(purchase_order, fg_item_code): + return frappe.db.sql(""" + SELECT + pois.main_item_code, + pois.rm_item_code, + item.description, + pois.required_qty AS qty, + pois.rate, + 1 as non_stock_item, + pois.stock_uom + FROM `tabPurchase Order Item Supplied` pois, `tabItem` item + WHERE + pois.`rm_item_code` = item.`name` + AND item.is_stock_item = 0 + AND pois.`parent` = %s + AND pois.`main_item_code` = %s + """, (purchase_order, fg_item_code), as_dict=1) + + +def set_serial_nos(raw_material, consumed_serial_nos, qty): + serial_nos = set(get_serial_nos(raw_material.serial_nos)) - \ + set(get_serial_nos(consumed_serial_nos)) + if serial_nos and qty <= len(serial_nos): + raw_material.serial_no = '\n'.join(list(serial_nos)[0:frappe.utils.cint(qty)]) + +def get_transferred_batch_qty_map(purchase_order, fg_item): + # returns + # { + # (item_code, fg_code): { + # batch1: 10, # qty + # batch2: 16 + # }, + # } + transferred_batch_qty_map = {} + transferred_batches = frappe.db.sql(""" + SELECT + sed.batch_no, + SUM(sed.qty) AS qty, + sed.item_code + FROM `tabStock Entry` se,`tabStock Entry Detail` sed + WHERE + se.name = sed.parent + AND se.docstatus=1 + AND se.purpose='Send to Subcontractor' + AND se.purchase_order = %s + AND sed.subcontracted_item = %s + AND sed.batch_no IS NOT NULL + GROUP BY + sed.batch_no, + sed.item_code + """, (purchase_order, fg_item), as_dict=1) + + for batch_data in transferred_batches: + transferred_batch_qty_map.setdefault((batch_data.item_code, fg_item), {}) + transferred_batch_qty_map[(batch_data.item_code, fg_item)][batch_data.batch_no] = batch_data.qty + + return transferred_batch_qty_map + +def get_backflushed_batch_qty_map(purchase_order, fg_item): + # returns + # { + # (item_code, fg_code): { + # batch1: 10, # qty + # batch2: 16 + # }, + # } + backflushed_batch_qty_map = {} + backflushed_batches = frappe.db.sql(""" + SELECT + pris.batch_no, + SUM(pris.consumed_qty) AS qty, + pris.rm_item_code AS item_code + FROM `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pri, `tabPurchase Receipt Item Supplied` pris + WHERE + pr.name = pri.parent + AND pri.parent = pris.parent + AND pri.purchase_order = %s + AND pri.item_code = pris.main_item_code + AND pr.docstatus = 1 + AND pris.main_item_code = %s + AND pris.batch_no IS NOT NULL + GROUP BY + pris.rm_item_code, pris.batch_no + """, (purchase_order, fg_item), as_dict=1) + + for batch_data in backflushed_batches: + backflushed_batch_qty_map.setdefault((batch_data.item_code, fg_item), {}) + backflushed_batch_qty_map[(batch_data.item_code, fg_item)][batch_data.batch_no] = batch_data.qty + + return backflushed_batch_qty_map + +def get_batches_with_qty(item_code, fg_item, required_qty, transferred_batch_qty_map, backflushed_batch_qty_map): + # Returns available batches to be backflushed based on requirements + transferred_batches = transferred_batch_qty_map.get((item_code, fg_item), {}) + backflushed_batches = backflushed_batch_qty_map.get((item_code, fg_item), {}) + + available_batches = [] + + for (batch, transferred_qty) in transferred_batches.items(): + backflushed_qty = backflushed_batches.get(batch, 0) + available_qty = transferred_qty - backflushed_qty + + if available_qty >= required_qty: + available_batches.append({'batch': batch, 'qty': required_qty}) + break + else: + available_batches.append({'batch': batch, 'qty': available_qty}) + required_qty -= available_qty + + return available_batches \ No newline at end of file diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index a9e50bab5a4..d18f8e54d8f 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -4,9 +4,9 @@ from __future__ import unicode_literals import frappe from frappe.desk.reportview import get_match_cond, get_filters_cond -from frappe.utils import nowdate +from frappe.utils import nowdate, getdate from collections import defaultdict - +from erpnext.stock.get_item_details import _get_item_tax_template # searches for active employees def employee_query(doctype, txt, searchfield, start, page_len, filters): @@ -159,8 +159,12 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals if "description" in searchfields: searchfields.remove("description") - columns = [field for field in searchfields if not field in ["name", "item_group", "description"]] - columns = ", ".join(columns) + columns = '' + extra_searchfields = [field for field in searchfields + if not field in ["name", "item_group", "description"]] + + if extra_searchfields: + columns = ", " + ", ".join(extra_searchfields) searchfields = searchfields + [field for field in[searchfield or "name", "item_code", "item_group", "item_name"] if not field in searchfields] @@ -176,7 +180,7 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals concat(substr(tabItem.item_name, 1, 40), "..."), item_name) as item_name, tabItem.item_group, if(length(tabItem.description) > 40, \ - concat(substr(tabItem.description, 1, 40), "..."), description) as description, + concat(substr(tabItem.description, 1, 40), "..."), description) as description {columns} from tabItem where tabItem.docstatus < 2 @@ -307,6 +311,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters): and sle.item_code = %(item_code)s and sle.warehouse = %(warehouse)s and (sle.batch_no like %(txt)s + or batch.expiry_date like %(txt)s or batch.manufacturing_date like %(txt)s) and batch.docstatus < 2 {cond} @@ -325,6 +330,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters): where batch.disabled = 0 and item = %(item_code)s and (name like %(txt)s + or expiry_date like %(txt)s or manufacturing_date like %(txt)s) and docstatus < 2 {0} @@ -476,3 +482,53 @@ def item_manufacturer_query(doctype, txt, searchfield, start, page_len, filters) as_list=1 ) return item_manufacturers + +@frappe.whitelist() +def get_purchase_receipts(doctype, txt, searchfield, start, page_len, filters): + query = """ + select pr.name + from `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pritem + where pr.docstatus = 1 and pritem.parent = pr.name + and pr.name like {txt}""".format(txt = frappe.db.escape('%{0}%'.format(txt))) + + if filters and filters.get('item_code'): + query += " and pritem.item_code = {item_code}".format(item_code = frappe.db.escape(filters.get('item_code'))) + + return frappe.db.sql(query, filters) + +@frappe.whitelist() +def get_purchase_invoices(doctype, txt, searchfield, start, page_len, filters): + query = """ + select pi.name + from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` piitem + where pi.docstatus = 1 and piitem.parent = pi.name + and pi.name like {txt}""".format(txt = frappe.db.escape('%{0}%'.format(txt))) + + if filters and filters.get('item_code'): + query += " and piitem.item_code = {item_code}".format(item_code = frappe.db.escape(filters.get('item_code'))) + + return frappe.db.sql(query, filters) + +@frappe.whitelist() +def get_tax_template(doctype, txt, searchfield, start, page_len, filters): + + item_doc = frappe.get_cached_doc('Item', filters.get('item_code')) + item_group = filters.get('item_group') + taxes = item_doc.taxes or [] + + while item_group: + item_group_doc = frappe.get_cached_doc('Item Group', item_group) + taxes += item_group_doc.taxes or [] + item_group = item_group_doc.parent_item_group + + if not taxes: + return frappe.db.sql(""" SELECT name FROM `tabItem Tax Template` """) + else: + args = { + 'item_code': filters.get('item_code'), + 'posting_date': filters.get('valid_from'), + 'tax_category': filters.get('tax_category') + } + + taxes = _get_item_tax_template(args, taxes, for_validate=True) + return [(d,) for d in set(taxes)] diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 859529204be..81fdbbefc35 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -72,7 +72,7 @@ def validate_returned_items(doc): items_returned = False for d in doc.get("items"): - if d.item_code and (flt(d.qty) < 0 or d.get('received_qty') < 0): + if d.item_code and (flt(d.qty) < 0 or flt(d.get('received_qty')) < 0): if d.item_code not in valid_items: frappe.throw(_("Row # {0}: Returned Item {1} does not exists in {2} {3}") .format(d.idx, d.item_code, doc.doctype, doc.return_against)) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 9dbd5be9188..9a9f3d1d319 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -148,13 +148,6 @@ class SellingController(StockController): if sales_team and total != 100.0: throw(_("Total allocated percentage for sales team should be 100")) - def validate_order_type(self): - valid_types = ["Sales", "Maintenance", "Shopping Cart"] - if not self.order_type: - self.order_type = "Sales" - elif self.order_type not in valid_types: - throw(_("Order Type must be one of {0}").format(comma_or(valid_types))) - def validate_max_discount(self): for d in self.get("items"): if d.item_code: diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 2b2c27b052f..8b275a64fb3 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -182,7 +182,7 @@ class StatusUpdater(Document): if args.get('no_allowance'): item['reduce_by'] = item[args['target_field']] - item[args['target_ref_field']] if item['reduce_by'] > .01: - self.limits_crossed_error(args, item) + self.limits_crossed_error(args, item, "qty") elif item[args['target_ref_field']]: self.check_overflow_with_allowance(item, args) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 542073ebd76..14ee23b112f 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -20,6 +20,7 @@ class StockController(AccountsController): def validate(self): super(StockController, self).validate() self.validate_inspection() + self.validate_serialized_batch() def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False): if self.docstatus == 2: @@ -42,6 +43,17 @@ class StockController(AccountsController): gl_entries = self.get_asset_gl_entry(gl_entries) make_gl_entries(gl_entries, from_repost=from_repost) + def validate_serialized_batch(self): + from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos + for d in self.get("items"): + if hasattr(d, 'serial_no') and hasattr(d, 'batch_no') and d.serial_no and d.batch_no: + serial_nos = get_serial_nos(d.serial_no) + for serial_no_data in frappe.get_all("Serial No", + filters={"name": ("in", serial_nos)}, fields=["batch_no", "name"]): + if serial_no_data.batch_no != d.batch_no: + frappe.throw(_("Row #{0}: Serial No {1} does not belong to Batch {2}") + .format(d.idx, serial_no_data.name, d.batch_no)) + def get_gl_entries(self, warehouse_account=None, default_expense_account=None, default_cost_center=None): @@ -54,6 +66,7 @@ class StockController(AccountsController): gl_list = [] warehouse_with_no_account = [] + precision = frappe.get_precision("GL Entry", "debit_in_account_currency") for item_row in voucher_details: sle_list = sle_map.get(item_row.name) if sle_list: @@ -79,7 +92,7 @@ class StockController(AccountsController): "against": item_row.expense_account, "cost_center": item_row.cost_center, "remarks": self.get("remarks") or "Accounting Entry for Stock", - "debit": flt(sle.stock_value_difference, 2), + "debit": flt(sle.stock_value_difference, precision), "is_opening": item_row.get("is_opening") or self.get("is_opening") or "No", }, warehouse_account[sle.warehouse]["account_currency"], item=item_row)) @@ -89,7 +102,7 @@ class StockController(AccountsController): "against": warehouse_account[sle.warehouse]["account"], "cost_center": item_row.cost_center, "remarks": self.get("remarks") or "Accounting Entry for Stock", - "credit": flt(sle.stock_value_difference, 2), + "credit": flt(sle.stock_value_difference, precision), "project": item_row.get("project") or self.get("project"), "is_opening": item_row.get("is_opening") or self.get("is_opening") or "No" }, item=item_row)) @@ -221,6 +234,17 @@ class StockController(AccountsController): frappe.throw(_("{0} {1}: Cost Center is mandatory for Item {2}").format( _(self.doctype), self.name, item.get("item_code"))) + def delete_auto_created_batches(self): + for d in self.items: + if not d.batch_no: continue + + d.batch_no = None + d.db_set("batch_no", None) + + for data in frappe.get_all("Batch", + {'reference_name': self.name, 'reference_doctype': self.doctype}): + frappe.delete_doc("Batch", data.name) + def get_sl_entries(self, d, args): sl_dict = frappe._dict({ "item_code": d.get("item_code", None), diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index d2db9d005a7..b52a07dbdf0 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -8,6 +8,7 @@ from frappe import _, scrub from frappe.utils import cint, flt, round_based_on_smallest_currency_fraction from erpnext.controllers.accounts_controller import validate_conversion_rate, \ validate_taxes_and_charges, validate_inclusive_tax +from erpnext.stock.get_item_details import _get_item_tax_template class calculate_taxes_and_totals(object): def __init__(self, doc): @@ -34,6 +35,7 @@ class calculate_taxes_and_totals(object): def _calculate(self): self.validate_conversion_rate() self.calculate_item_values() + self.validate_item_tax_template() self.initialize_taxes() self.determine_exclusive_rate() self.calculate_net_total() @@ -43,6 +45,38 @@ class calculate_taxes_and_totals(object): self._cleanup() self.calculate_total_net_weight() + def validate_item_tax_template(self): + for item in self.doc.get('items'): + if item.item_code and item.get('item_tax_template'): + item_doc = frappe.get_cached_doc("Item", item.item_code) + args = { + 'tax_category': self.doc.get('tax_category'), + 'posting_date': self.doc.get('posting_date'), + 'bill_date': self.doc.get('bill_date'), + 'transaction_date': self.doc.get('transaction_date') + } + + item_group = item_doc.item_group + item_group_taxes = [] + + while item_group: + item_group_doc = frappe.get_cached_doc('Item Group', item_group) + item_group_taxes += item_group_doc.taxes or [] + item_group = item_group_doc.parent_item_group + + item_taxes = item_doc.taxes or [] + + if not item_group_taxes and (not item_taxes): + # No validation if no taxes in item or item group + continue + + taxes = _get_item_tax_template(args, item_taxes + item_group_taxes, for_validate=True) + + if item.item_tax_template not in taxes: + frappe.throw(_("Row {0}: Invalid Item Tax Template for item {1}").format( + item.idx, frappe.bold(item.item_code) + )) + def validate_conversion_rate(self): # validate conversion rate company_currency = erpnext.get_company_currency(self.doc.company) @@ -312,11 +346,19 @@ class calculate_taxes_and_totals(object): last_tax = self.doc.get("taxes")[-1] non_inclusive_tax_amount = sum([flt(d.tax_amount_after_discount_amount) for d in self.doc.get("taxes") if not d.included_in_print_rate]) + diff = self.doc.total + non_inclusive_tax_amount \ - flt(last_tax.total, last_tax.precision("total")) + + # If discount amount applied, deduct the discount amount + # because self.doc.total is always without discount, but last_tax.total is after discount + if self.discount_amount_applied and self.doc.discount_amount: + diff -= flt(self.doc.discount_amount) + + diff = flt(diff, self.doc.precision("rounding_adjustment")) + if diff and abs(diff) <= (5.0 / 10**last_tax.precision("tax_amount")): - self.doc.rounding_adjustment = flt(flt(self.doc.rounding_adjustment) + - flt(diff), self.doc.precision("rounding_adjustment")) + self.doc.rounding_adjustment = diff def calculate_totals(self): self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment) \ @@ -552,7 +594,7 @@ class calculate_taxes_and_totals(object): if item.price_list_rate: if item.pricing_rules and not self.doc.ignore_pricing_rule: for d in item.pricing_rules.split(','): - pricing_rule = frappe.get_doc('Pricing Rule', d) + pricing_rule = frappe.get_cached_doc('Pricing Rule', d) if (pricing_rule.margin_type == 'Amount' and pricing_rule.currency == self.doc.currency)\ or (pricing_rule.margin_type == 'Percentage'): diff --git a/erpnext/setup/doctype/setup_progress_action/__init__.py b/erpnext/crm/doctype/appointment/__init__.py similarity index 100% rename from erpnext/setup/doctype/setup_progress_action/__init__.py rename to erpnext/crm/doctype/appointment/__init__.py diff --git a/erpnext/crm/doctype/appointment/appointment.js b/erpnext/crm/doctype/appointment/appointment.js new file mode 100644 index 00000000000..ca38121b1cf --- /dev/null +++ b/erpnext/crm/doctype/appointment/appointment.js @@ -0,0 +1,26 @@ +// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Appointment', { + refresh: function(frm) { + if(frm.doc.lead){ + frm.add_custom_button(frm.doc.lead,()=>{ + frappe.set_route("Form", "Lead", frm.doc.lead); + }); + } + if(frm.doc.calendar_event){ + frm.add_custom_button(__(frm.doc.calendar_event),()=>{ + frappe.set_route("Form", "Event", frm.doc.calendar_event); + }); + } + }, + onload: function(frm){ + frm.set_query("appointment_with", function(){ + return { + filters : { + "name": ["in", ["Customer", "Lead"]] + } + }; + }); + } +}); diff --git a/erpnext/crm/doctype/appointment/appointment.json b/erpnext/crm/doctype/appointment/appointment.json new file mode 100644 index 00000000000..8517ddec321 --- /dev/null +++ b/erpnext/crm/doctype/appointment/appointment.json @@ -0,0 +1,162 @@ +{ + "actions": [], + "autoname": "format:APMT-{customer_name}-{####}", + "creation": "2019-08-27 10:48:27.926283", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "scheduled_time", + "status", + "customer_details_section", + "customer_name", + "customer_phone_number", + "customer_skype", + "customer_email", + "col_br_2", + "customer_details", + "linked_docs_section", + "appointment_with", + "party", + "col_br_3", + "calendar_event" + ], + "fields": [ + { + "fieldname": "customer_details_section", + "fieldtype": "Section Break", + "label": "Customer Details" + }, + { + "fieldname": "customer_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Name", + "reqd": 1 + }, + { + "fieldname": "customer_phone_number", + "fieldtype": "Data", + "label": "Phone Number" + }, + { + "fieldname": "customer_skype", + "fieldtype": "Data", + "label": "Skype ID" + }, + { + "fieldname": "customer_details", + "fieldtype": "Long Text", + "label": "Details" + }, + { + "fieldname": "scheduled_time", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "Scheduled Time", + "reqd": 1 + }, + { + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "options": "Open\nUnverified\nClosed", + "reqd": 1 + }, + { + "fieldname": "calendar_event", + "fieldtype": "Link", + "label": "Calendar Event", + "options": "Event" + }, + { + "fieldname": "col_br_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "customer_email", + "fieldtype": "Data", + "label": "Email", + "reqd": 1 + }, + { + "fieldname": "linked_docs_section", + "fieldtype": "Section Break", + "label": "Linked Documents" + }, + { + "fieldname": "col_br_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "appointment_with", + "fieldtype": "Link", + "label": "Appointment With", + "options": "DocType" + }, + { + "fieldname": "party", + "fieldtype": "Dynamic Link", + "label": "Party", + "options": "appointment_with" + } + ], + "links": [], + "modified": "2020-01-28 16:16:45.447213", + "modified_by": "Administrator", + "module": "CRM", + "name": "Appointment", + "name_case": "UPPER CASE", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Guest", + "share": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales User", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/crm/doctype/appointment/appointment.py b/erpnext/crm/doctype/appointment/appointment.py new file mode 100644 index 00000000000..63efeb3cb61 --- /dev/null +++ b/erpnext/crm/doctype/appointment/appointment.py @@ -0,0 +1,238 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals + +import urllib +from collections import Counter +from datetime import timedelta + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import get_url, getdate +from frappe.utils.verified_command import verify_request, get_signed_params + + +class Appointment(Document): + + def find_lead_by_email(self): + lead_list = frappe.get_list( + 'Lead', filters={'email_id': self.customer_email}, ignore_permissions=True) + if lead_list: + return lead_list[0].name + return None + + def find_customer_by_email(self): + customer_list = frappe.get_list( + 'Customer', filters={'email_id': self.customer_email}, ignore_permissions=True + ) + if customer_list: + return customer_list[0].name + return None + + def before_insert(self): + number_of_appointments_in_same_slot = frappe.db.count( + 'Appointment', filters={'scheduled_time': self.scheduled_time}) + number_of_agents = frappe.db.get_single_value('Appointment Booking Settings', 'number_of_agents') + if not number_of_agents == 0: + if (number_of_appointments_in_same_slot >= number_of_agents): + frappe.throw('Time slot is not available') + # Link lead + if not self.party: + lead = self.find_lead_by_email() + customer = self.find_customer_by_email() + if customer: + self.appointment_with = "Customer" + self.party = customer + else: + self.appointment_with = "Lead" + self.party = lead + + def after_insert(self): + if self.party: + # Create Calendar event + self.auto_assign() + self.create_calendar_event() + else: + # Set status to unverified + self.status = 'Unverified' + # Send email to confirm + self.send_confirmation_email() + + def send_confirmation_email(self): + verify_url = self._get_verify_url() + template = 'confirm_appointment' + args = { + "link":verify_url, + "site_url":frappe.utils.get_url(), + "full_name":self.customer_name, + } + frappe.sendmail(recipients=[self.customer_email], + template=template, + args=args, + subject=_('Appointment Confirmation')) + if frappe.session.user == "Guest": + frappe.msgprint( + 'Please check your email to confirm the appointment') + else : + frappe.msgprint( + 'Appointment was created. But no lead was found. Please check the email to confirm') + + def on_change(self): + # Sync Calendar + if not self.calendar_event: + return + cal_event = frappe.get_doc('Event', self.calendar_event) + cal_event.starts_on = self.scheduled_time + cal_event.save(ignore_permissions=True) + + + def set_verified(self, email): + if not email == self.customer_email: + frappe.throw('Email verification failed.') + # Create new lead + self.create_lead_and_link() + # Remove unverified status + self.status = 'Open' + # Create calender event + self.auto_assign() + self.create_calendar_event() + self.save(ignore_permissions=True) + frappe.db.commit() + + def create_lead_and_link(self): + # Return if already linked + if self.party: + return + lead = frappe.get_doc({ + 'doctype': 'Lead', + 'lead_name': self.customer_name, + 'email_id': self.customer_email, + 'notes': self.customer_details, + 'phone': self.customer_phone_number, + }) + lead.insert(ignore_permissions=True) + # Link lead + self.party = lead.name + + def auto_assign(self): + from frappe.desk.form.assign_to import add as add_assignemnt + existing_assignee = self.get_assignee_from_latest_opportunity() + if existing_assignee: + # If the latest opportunity is assigned to someone + # Assign the appointment to the same + add_assignemnt({ + 'doctype': self.doctype, + 'name': self.name, + 'assign_to': existing_assignee + }) + return + if self._assign: + return + available_agents = _get_agents_sorted_by_asc_workload( + getdate(self.scheduled_time)) + for agent in available_agents: + if(_check_agent_availability(agent, self.scheduled_time)): + agent = agent[0] + add_assignemnt({ + 'doctype': self.doctype, + 'name': self.name, + 'assign_to': agent + }) + break + + def get_assignee_from_latest_opportunity(self): + if not self.party: + return None + if not frappe.db.exists('Lead', self.party): + return None + opporutnities = frappe.get_list( + 'Opportunity', + filters={ + 'party_name': self.party, + }, + ignore_permissions=True, + order_by='creation desc') + if not opporutnities: + return None + latest_opportunity = frappe.get_doc('Opportunity', opporutnities[0].name ) + assignee = latest_opportunity._assign + if not assignee: + return None + assignee = frappe.parse_json(assignee)[0] + return assignee + + def create_calendar_event(self): + if self.calendar_event: + return + appointment_event = frappe.get_doc({ + 'doctype': 'Event', + 'subject': ' '.join(['Appointment with', self.customer_name]), + 'starts_on': self.scheduled_time, + 'status': 'Open', + 'type': 'Public', + 'send_reminder': frappe.db.get_single_value('Appointment Booking Settings', 'email_reminders'), + 'event_participants': [dict(reference_doctype=self.appointment_with, reference_docname=self.party)] + }) + employee = _get_employee_from_user(self._assign) + if employee: + appointment_event.append('event_participants', dict( + reference_doctype='Employee', + reference_docname=employee.name)) + appointment_event.insert(ignore_permissions=True) + self.calendar_event = appointment_event.name + self.save(ignore_permissions=True) + + def _get_verify_url(self): + verify_route = '/book_appointment/verify' + params = { + 'email': self.customer_email, + 'appointment': self.name + } + return get_url(verify_route + '?' + get_signed_params(params)) + + +def _get_agents_sorted_by_asc_workload(date): + appointments = frappe.db.get_list('Appointment', fields='*') + agent_list = _get_agent_list_as_strings() + if not appointments: + return agent_list + appointment_counter = Counter(agent_list) + for appointment in appointments: + assigned_to = frappe.parse_json(appointment._assign) + if not assigned_to: + continue + if (assigned_to[0] in agent_list) and getdate(appointment.scheduled_time) == date: + appointment_counter[assigned_to[0]] += 1 + sorted_agent_list = appointment_counter.most_common() + sorted_agent_list.reverse() + return sorted_agent_list + + +def _get_agent_list_as_strings(): + agent_list_as_strings = [] + agent_list = frappe.get_doc('Appointment Booking Settings').agent_list + for agent in agent_list: + agent_list_as_strings.append(agent.user) + return agent_list_as_strings + + +def _check_agent_availability(agent_email, scheduled_time): + appointemnts_at_scheduled_time = frappe.get_list( + 'Appointment', filters={'scheduled_time': scheduled_time}) + for appointment in appointemnts_at_scheduled_time: + if appointment._assign == agent_email: + return False + return True + + +def _get_employee_from_user(user): + employee_docname = frappe.db.exists( + {'doctype': 'Employee', 'user_id': user}) + if employee_docname: + # frappe.db.exists returns a tuple of a tuple + return frappe.get_doc('Employee', employee_docname[0][0]) + return None + diff --git a/erpnext/crm/doctype/appointment/test_appointment.py b/erpnext/crm/doctype/appointment/test_appointment.py new file mode 100644 index 00000000000..50c98c59de6 --- /dev/null +++ b/erpnext/crm/doctype/appointment/test_appointment.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest +import datetime + + +def create_test_lead(): + test_lead = frappe.db.exists({'doctype': 'Lead', 'lead_name': 'Test Lead'}) + if test_lead: + return frappe.get_doc('Lead', test_lead[0][0]) + test_lead = frappe.get_doc({ + 'doctype': 'Lead', + 'lead_name': 'Test Lead', + 'email_id': 'test@example.com' + }) + test_lead.insert(ignore_permissions=True) + return test_lead + + +def create_test_appointments(): + test_appointment = frappe.db.exists( + {'doctype': 'Appointment', 'scheduled_time':datetime.datetime.now(),'email':'test@example.com'}) + if test_appointment: + return frappe.get_doc('Appointment', test_appointment[0][0]) + test_appointment = frappe.get_doc({ + 'doctype': 'Appointment', + 'email': 'test@example.com', + 'status': 'Open', + 'customer_name': 'Test Lead', + 'customer_phone_number': '666', + 'customer_skype': 'test', + 'customer_email': 'test@example.com', + 'scheduled_time': datetime.datetime.now() + }) + test_appointment.insert() + return test_appointment + + +class TestAppointment(unittest.TestCase): + test_appointment = test_lead = None + + def setUp(self): + self.test_lead = create_test_lead() + self.test_appointment = create_test_appointments() + + def test_calendar_event_created(self): + cal_event = frappe.get_doc( + 'Event', self.test_appointment.calendar_event) + self.assertEqual(cal_event.starts_on, + self.test_appointment.scheduled_time) + + def test_lead_linked(self): + lead = frappe.get_doc('Lead', self.test_lead.name) + self.assertIsNotNone(lead) diff --git a/erpnext/crm/doctype/appointment_booking_settings/__init__.py b/erpnext/crm/doctype/appointment_booking_settings/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js new file mode 100644 index 00000000000..99b82148d2e --- /dev/null +++ b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js @@ -0,0 +1,10 @@ +frappe.ui.form.on('Appointment Booking Settings', 'validate',check_times); +function check_times(frm) { + $.each(frm.doc.availability_of_slots || [], function (i, d) { + let from_time = Date.parse('01/01/2019 ' + d.from_time); + let to_time = Date.parse('01/01/2019 ' + d.to_time); + if (from_time > to_time) { + frappe.throw(__(`In row ${i + 1} of Appointment Booking Slots : "To Time" must be later than "From Time"`)); + } + }); +} \ No newline at end of file diff --git a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json new file mode 100644 index 00000000000..4b26e4901bd --- /dev/null +++ b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.json @@ -0,0 +1,151 @@ +{ + "creation": "2019-08-27 10:56:48.309824", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "enable_scheduling", + "agent_detail_section", + "availability_of_slots", + "number_of_agents", + "agent_list", + "holiday_list", + "appointment_details_section", + "appointment_duration", + "email_reminders", + "advance_booking_days", + "success_details", + "success_redirect_url" + ], + "fields": [ + { + "fieldname": "availability_of_slots", + "fieldtype": "Table", + "label": "Availability Of Slots", + "options": "Appointment Booking Slots", + "reqd": 1 + }, + { + "default": "1", + "fieldname": "number_of_agents", + "fieldtype": "Int", + "hidden": 1, + "in_list_view": 1, + "label": "Number of Concurrent Appointments", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "holiday_list", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Holiday List", + "options": "Holiday List", + "reqd": 1 + }, + { + "default": "60", + "fieldname": "appointment_duration", + "fieldtype": "Int", + "label": "Appointment Duration (In Minutes)", + "reqd": 1 + }, + { + "default": "0", + "description": "Notify customer and agent via email on the day of the appointment.", + "fieldname": "email_reminders", + "fieldtype": "Check", + "label": "Notify Via Email" + }, + { + "default": "7", + "fieldname": "advance_booking_days", + "fieldtype": "Int", + "label": "Number of days appointments can be booked in advance", + "reqd": 1 + }, + { + "fieldname": "agent_list", + "fieldtype": "Table MultiSelect", + "label": "Agents", + "options": "Assignment Rule User", + "reqd": 1 + }, + { + "default": "0", + "fieldname": "enable_scheduling", + "fieldtype": "Check", + "label": "Enable Appointment Scheduling", + "reqd": 1 + }, + { + "fieldname": "agent_detail_section", + "fieldtype": "Section Break", + "label": "Agent Details" + }, + { + "fieldname": "appointment_details_section", + "fieldtype": "Section Break", + "label": "Appointment Details" + }, + { + "fieldname": "success_details", + "fieldtype": "Section Break", + "label": "Success Settings" + }, + { + "description": "Leave blank for home.\nThis is relative to site URL, for example \"about\" will redirect to \"https://yoursitename.com/about\"", + "fieldname": "success_redirect_url", + "fieldtype": "Data", + "label": "Success Redirect URL" + } + ], + "issingle": 1, + "modified": "2019-11-26 12:14:17.669366", + "modified_by": "Administrator", + "module": "CRM", + "name": "Appointment Booking Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "role": "Guest", + "share": 1 + }, + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "Sales Manager", + "share": 1, + "write": 1 + } + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py new file mode 100644 index 00000000000..27f14b1dbd8 --- /dev/null +++ b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe import _ +import datetime +from frappe.model.document import Document + + +class AppointmentBookingSettings(Document): + agent_list = [] #Hack + min_date = '01/01/1970 ' + format_string = "%d/%m/%Y %H:%M:%S" + + def validate(self): + self.validate_availability_of_slots() + + def save(self): + self.number_of_agents = len(self.agent_list) + super(AppointmentBookingSettings, self).save() + + def validate_availability_of_slots(self): + for record in self.availability_of_slots: + from_time = datetime.datetime.strptime( + self.min_date+record.from_time, self.format_string) + to_time = datetime.datetime.strptime( + self.min_date+record.to_time, self.format_string) + timedelta = to_time-from_time + self.validate_from_and_to_time(from_time, to_time) + self.duration_is_divisible(from_time, to_time) + + def validate_from_and_to_time(self, from_time, to_time): + if from_time > to_time: + err_msg = _('From Time cannot be later than To Time for {0}').format(record.day_of_week) + frappe.throw(_(err_msg)) + + def duration_is_divisible(self, from_time, to_time): + timedelta = to_time - from_time + if timedelta.total_seconds() % (self.appointment_duration * 60): + frappe.throw( + _('The difference between from time and To Time must be a multiple of Appointment')) diff --git a/erpnext/crm/doctype/appointment_booking_settings/test_appointment_booking_settings.py b/erpnext/crm/doctype/appointment_booking_settings/test_appointment_booking_settings.py new file mode 100644 index 00000000000..3dc3c399712 --- /dev/null +++ b/erpnext/crm/doctype/appointment_booking_settings/test_appointment_booking_settings.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestAppointmentBookingSettings(unittest.TestCase): + pass diff --git a/erpnext/crm/doctype/appointment_booking_slots/__init__.py b/erpnext/crm/doctype/appointment_booking_slots/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/crm/doctype/appointment_booking_slots/appointment_booking_slots.json b/erpnext/crm/doctype/appointment_booking_slots/appointment_booking_slots.json new file mode 100644 index 00000000000..ddf87386295 --- /dev/null +++ b/erpnext/crm/doctype/appointment_booking_slots/appointment_booking_slots.json @@ -0,0 +1,46 @@ +{ + "creation": "2019-11-19 10:49:49.494927", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "day_of_week", + "from_time", + "to_time" + ], + "fields": [ + { + "fieldname": "day_of_week", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Day Of Week", + "options": "Sunday\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday", + "reqd": 1 + }, + { + "fieldname": "from_time", + "fieldtype": "Time", + "in_list_view": 1, + "label": "From Time ", + "reqd": 1 + }, + { + "fieldname": "to_time", + "fieldtype": "Time", + "in_list_view": 1, + "label": "To Time", + "reqd": 1 + } + ], + "istable": 1, + "modified": "2019-11-19 10:49:49.494927", + "modified_by": "Administrator", + "module": "CRM", + "name": "Appointment Booking Slots", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/crm/doctype/appointment_booking_slots/appointment_booking_slots.py b/erpnext/crm/doctype/appointment_booking_slots/appointment_booking_slots.py new file mode 100644 index 00000000000..3cadbc95590 --- /dev/null +++ b/erpnext/crm/doctype/appointment_booking_slots/appointment_booking_slots.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class AppointmentBookingSlots(Document): + pass diff --git a/erpnext/crm/doctype/availability_of_slots/__init__.py b/erpnext/crm/doctype/availability_of_slots/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/crm/doctype/availability_of_slots/availability_of_slots.json b/erpnext/crm/doctype/availability_of_slots/availability_of_slots.json new file mode 100644 index 00000000000..b54af8dba4f --- /dev/null +++ b/erpnext/crm/doctype/availability_of_slots/availability_of_slots.json @@ -0,0 +1,46 @@ +{ + "creation": "2019-09-10 15:02:05.779434", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "day_of_week", + "from_time", + "to_time" + ], + "fields": [ + { + "fieldname": "day_of_week", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Day Of Week", + "options": "Sunday\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday", + "reqd": 1 + }, + { + "fieldname": "from_time", + "fieldtype": "Time", + "in_list_view": 1, + "label": "From Time", + "reqd": 1 + }, + { + "fieldname": "to_time", + "fieldtype": "Time", + "in_list_view": 1, + "label": "To Time", + "reqd": 1 + } + ], + "istable": 1, + "modified": "2019-09-10 15:05:20.406855", + "modified_by": "Administrator", + "module": "CRM", + "name": "Availability Of Slots", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/crm/doctype/availability_of_slots/availability_of_slots.py b/erpnext/crm/doctype/availability_of_slots/availability_of_slots.py new file mode 100644 index 00000000000..8258471eed1 --- /dev/null +++ b/erpnext/crm/doctype/availability_of_slots/availability_of_slots.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class AvailabilityOfSlots(Document): + pass diff --git a/erpnext/crm/doctype/email_campaign/email_campaign.json b/erpnext/crm/doctype/email_campaign/email_campaign.json index 32591362753..736a9d61736 100644 --- a/erpnext/crm/doctype/email_campaign/email_campaign.json +++ b/erpnext/crm/doctype/email_campaign/email_campaign.json @@ -52,7 +52,8 @@ "fieldtype": "Select", "in_list_view": 1, "label": "Email Campaign For ", - "options": "\nLead\nContact" + "options": "\nLead\nContact", + "reqd": 1 }, { "fieldname": "recipient", @@ -69,7 +70,7 @@ "options": "User" } ], - "modified": "2019-07-12 13:47:37.261213", + "modified": "2019-11-11 17:18:47.342839", "modified_by": "Administrator", "module": "CRM", "name": "Email Campaign", diff --git a/erpnext/crm/doctype/email_campaign/email_campaign.py b/erpnext/crm/doctype/email_campaign/email_campaign.py index 98e4927beb6..00a4bd1a322 100644 --- a/erpnext/crm/doctype/email_campaign/email_campaign.py +++ b/erpnext/crm/doctype/email_campaign/email_campaign.py @@ -41,7 +41,8 @@ class EmailCampaign(Document): email_campaign_exists = frappe.db.exists("Email Campaign", { "campaign_name": self.campaign_name, "recipient": self.recipient, - "status": ("in", ["In Progress", "Scheduled"]) + "status": ("in", ["In Progress", "Scheduled"]), + "name": ("!=", self.name) }) if email_campaign_exists: frappe.throw(_("The Campaign '{0}' already exists for the {1} '{2}'").format(self.campaign_name, self.email_campaign_for, self.recipient)) @@ -73,13 +74,13 @@ def send_mail(entry, email_campaign): email_template = frappe.get_doc("Email Template", entry.get("email_template")) sender = frappe.db.get_value("User", email_campaign.get("sender"), 'email') - + context = {"doc": frappe.get_doc(email_campaign.email_campaign_for, email_campaign.recipient)} # send mail and link communication to document comm = make( doctype = "Email Campaign", name = email_campaign.name, - subject = email_template.get("subject"), - content = email_template.get("response"), + subject = frappe.render_template(email_template.get("subject"), context), + content = frappe.render_template(email_template.get("response"), context), sender = sender, recipients = recipient, communication_medium = "Email", diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index 122e2b4eee8..0c88d2826f7 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -1,4 +1,4 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt frappe.provide("erpnext"); @@ -7,57 +7,54 @@ cur_frm.email_field = "email_id"; erpnext.LeadController = frappe.ui.form.Controller.extend({ setup: function () { this.frm.make_methods = { + 'Customer': this.make_customer, 'Quotation': this.make_quotation, - 'Opportunity': this.create_opportunity - } - - this.frm.fields_dict.customer.get_query = function (doc, cdt, cdn) { - return { query: "erpnext.controllers.queries.customer_query" } - } + 'Opportunity': this.make_opportunity + }; this.frm.toggle_reqd("lead_name", !this.frm.doc.organization_lead); }, onload: function () { - if (cur_frm.fields_dict.lead_owner.df.options.match(/^User/)) { - cur_frm.fields_dict.lead_owner.get_query = function (doc, cdt, cdn) { - return { query: "frappe.core.doctype.user.user.user_query" } - } - } + this.frm.set_query("customer", function (doc, cdt, cdn) { + return { query: "erpnext.controllers.queries.customer_query" } + }); - if (cur_frm.fields_dict.contact_by.df.options.match(/^User/)) { - cur_frm.fields_dict.contact_by.get_query = function (doc, cdt, cdn) { - return { query: "frappe.core.doctype.user.user.user_query" } - } - } + this.frm.set_query("lead_owner", function (doc, cdt, cdn) { + return { query: "frappe.core.doctype.user.user.user_query" } + }); + + this.frm.set_query("contact_by", function (doc, cdt, cdn) { + return { query: "frappe.core.doctype.user.user.user_query" } + }); }, refresh: function () { - var doc = this.frm.doc; + let doc = this.frm.doc; erpnext.toggle_naming_series(); frappe.dynamic_link = { doc: doc, fieldname: 'name', doctype: 'Lead' } - if(!doc.__islocal && doc.__onload && !doc.__onload.is_customer) { - this.frm.add_custom_button(__("Customer"), this.create_customer, __('Create')); - this.frm.add_custom_button(__("Opportunity"), this.create_opportunity, __('Create')); - this.frm.add_custom_button(__("Quotation"), this.make_quotation, __('Create')); + if (!this.frm.is_new() && doc.__onload && !doc.__onload.is_customer) { + this.frm.add_custom_button(__("Customer"), this.make_customer, __("Create")); + this.frm.add_custom_button(__("Opportunity"), this.make_opportunity, __("Create")); + this.frm.add_custom_button(__("Quotation"), this.make_quotation, __("Create")); } - if (!this.frm.doc.__islocal) { - frappe.contacts.render_address_and_contact(cur_frm); + if (!this.frm.is_new()) { + frappe.contacts.render_address_and_contact(this.frm); } else { - frappe.contacts.clear_address_and_contact(cur_frm); + frappe.contacts.clear_address_and_contact(this.frm); } }, - create_customer: function () { + make_customer: function () { frappe.model.open_mapped_doc({ method: "erpnext.crm.doctype.lead.lead.make_customer", frm: cur_frm }) }, - create_opportunity: function () { + make_opportunity: function () { frappe.model.open_mapped_doc({ method: "erpnext.crm.doctype.lead.lead.make_opportunity", frm: cur_frm @@ -77,7 +74,7 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({ }, company_name: function () { - if (this.frm.doc.organization_lead == 1) { + if (this.frm.doc.organization_lead && !this.frm.doc.lead_name) { this.frm.set_value("lead_name", this.frm.doc.company_name); } }, @@ -85,7 +82,7 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({ contact_date: function () { if (this.frm.doc.contact_date) { let d = moment(this.frm.doc.contact_date); - d.add(1, "hours"); + d.add(1, "day"); this.frm.set_value("ends_on", d.format(frappe.defaultDatetimeFormat)); } } diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json index 3c22dc71999..bc007b146f1 100644 --- a/erpnext/crm/doctype/lead/lead.json +++ b/erpnext/crm/doctype/lead/lead.json @@ -1,1436 +1,445 @@ { - "allow_copy": 0, + "actions": [], "allow_events_in_timeline": 1, - "allow_guest_to_view": 0, "allow_import": 1, - "allow_rename": 0, "autoname": "naming_series:", - "beta": 0, "creation": "2013-04-10 11:45:37", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Document", - "editable_grid": 0, + "engine": "InnoDB", + "field_order": [ + "organization_lead", + "lead_details", + "naming_series", + "lead_name", + "company_name", + "email_id", + "col_break123", + "lead_owner", + "status", + "salutation", + "designation", + "gender", + "source", + "customer", + "campaign_name", + "image", + "section_break_12", + "contact_by", + "column_break_14", + "contact_date", + "ends_on", + "notes_section", + "notes", + "address_info", + "address_html", + "address_title", + "address_line1", + "address_line2", + "city", + "county", + "column_break2", + "contact_html", + "state", + "country", + "pincode", + "contact_section", + "phone", + "mobile_no", + "fax", + "more_info", + "type", + "market_segment", + "industry", + "request_type", + "column_break3", + "company", + "website", + "territory", + "unsubscribed", + "blog_subscriber", + "title" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, + "default": "0", "fieldname": "organization_lead", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Lead is an Organization", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 + "set_only_once": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "lead_details", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "options": "fa fa-user", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "fa fa-user" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fetch_if_empty": 0, "fieldname": "naming_series", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Series", - "length": 0, "no_copy": 1, "oldfieldname": "naming_series", "oldfieldtype": "Select", "options": "CRM-LEAD-.YYYY.-", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 + "set_only_once": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.organization_lead", - "fetch_if_empty": 0, "fieldname": "lead_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Person Name", - "length": 0, - "no_copy": 0, "oldfieldname": "lead_name", "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "company_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Organization Name", - "length": 0, - "no_copy": 0, "oldfieldname": "company_name", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Data" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "email_id", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Email Address", - "length": 0, - "no_copy": 0, "oldfieldname": "email_id", "oldfieldtype": "Data", "options": "Email", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "col_break123", "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50%" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "__user", - "fetch_if_empty": 0, "fieldname": "lead_owner", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Lead Owner", - "length": 0, - "no_copy": 0, "oldfieldname": "lead_owner", "oldfieldtype": "Link", "options": "User", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Lead", - "fetch_if_empty": 0, "fieldname": "status", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "Status", - "length": 0, "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", "options": "Lead\nOpen\nReplied\nOpportunity\nQuotation\nLost Quotation\nInterested\nConverted\nDo Not Contact", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 + }, + { + "depends_on": "eval: doc.__islocal", + "fieldname": "salutation", + "fieldtype": "Link", + "label": "Salutation", + "options": "Salutation" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.organization_lead", - "fetch_if_empty": 0, "fieldname": "gender", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Gender", - "length": 0, - "no_copy": 0, - "options": "Gender", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Gender" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, "fieldname": "source", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Source", - "length": 0, - "no_copy": 0, "oldfieldname": "source", "oldfieldtype": "Select", - "options": "Lead Source", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Lead Source" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.source == 'Existing Customer'", - "fetch_if_empty": 0, "fieldname": "customer", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "From Customer", - "length": 0, "no_copy": 1, "oldfieldname": "customer", "oldfieldtype": "Link", - "options": "Customer", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Customer" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval: doc.source==\"Campaign\"", - "description": "", - "fetch_if_empty": 0, "fieldname": "campaign_name", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Campaign Name", - "length": 0, - "no_copy": 0, "oldfieldname": "campaign_name", "oldfieldtype": "Link", - "options": "Campaign", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Campaign" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "image", "fieldtype": "Attach Image", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Image", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "print_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "section_break_12", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Follow Up", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Follow Up" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "contact_by", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Next Contact By", - "length": 0, - "no_copy": 0, "oldfieldname": "contact_by", "oldfieldtype": "Link", "options": "User", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "100px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break_14", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, - "description": "", - "fetch_if_empty": 0, "fieldname": "contact_date", "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Next Contact Date", - "length": 0, "no_copy": 1, "oldfieldname": "contact_date", "oldfieldtype": "Date", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "100px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "ends_on", "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Ends On", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "no_copy": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "notes_section", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Notes", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Notes" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "notes", "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Notes", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Notes" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "contact_info", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Address & Contact", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Column Break", - "options": "fa fa-map-marker", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.__islocal", - "fetch_if_empty": 0, - "fieldname": "address_desc", - "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Address Desc", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "address_html", "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Address HTML", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 + }, + { + "depends_on": "eval: doc.__islocal", + "fieldname": "address_title", + "fieldtype": "Data", + "label": "Address Title" + }, + { + "depends_on": "eval: doc.__islocal", + "fieldname": "address_line1", + "fieldtype": "Data", + "label": "Address Line 1" + }, + { + "depends_on": "eval: doc.__islocal", + "fieldname": "address_line2", + "fieldtype": "Data", + "label": "Address Line 2" + }, + { + "depends_on": "eval: doc.__islocal", + "fieldname": "city", + "fieldtype": "Data", + "label": "City/Town" + }, + { + "depends_on": "eval: doc.__islocal", + "fieldname": "county", + "fieldtype": "Data", + "label": "County" + }, + { + "depends_on": "eval: doc.__islocal", + "fieldname": "state", + "fieldtype": "Data", + "label": "State" + }, + { + "depends_on": "eval: doc.__islocal", + "fieldname": "country", + "fieldtype": "Link", + "label": "Country", + "options": "Country" + }, + { + "depends_on": "eval: doc.__islocal", + "fieldname": "pincode", + "fieldtype": "Data", + "label": "Postal Code", + "options": "Country" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.organization_lead", - "fetch_if_empty": 0, "fieldname": "contact_html", "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Contact HTML", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.organization_lead", - "fetch_if_empty": 0, + "depends_on": "eval: doc.__islocal", "fieldname": "phone", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Phone", - "length": 0, - "no_copy": 0, "oldfieldname": "contact_no", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Data" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.organization_lead", - "fetch_if_empty": 0, - "fieldname": "salutation", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Salutation", - "length": 0, - "no_copy": 0, - "options": "Salutation", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.organization_lead", - "fetch_if_empty": 0, + "depends_on": "eval: doc.__islocal", "fieldname": "mobile_no", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Mobile No.", - "length": 0, - "no_copy": 0, "oldfieldname": "mobile_no", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Data" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.organization_lead", - "fetch_if_empty": 0, + "depends_on": "eval: doc.__islocal", "fieldname": "fax", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Fax", - "length": 0, - "no_copy": 0, "oldfieldname": "fax", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "oldfieldtype": "Data" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "website", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Website", - "length": 0, - "no_copy": 0, - "oldfieldname": "website", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fetch_if_empty": 0, - "fieldname": "territory", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Territory", - "length": 0, - "no_copy": 0, - "oldfieldname": "territory", - "oldfieldtype": "Link", - "options": "Territory", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "more_info", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "More Information", - "length": 0, - "no_copy": 0, "oldfieldtype": "Section Break", - "options": "fa fa-file-text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "fa fa-file-text" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "type", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Lead Type", - "length": 0, - "no_copy": 0, "oldfieldname": "type", "oldfieldtype": "Select", - "options": "\nClient\nChannel Partner\nConsultant", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "\nClient\nChannel Partner\nConsultant" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "market_segment", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Market Segment", - "length": 0, - "no_copy": 0, "oldfieldname": "market_segment", "oldfieldtype": "Select", - "options": "Market Segment", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Market Segment" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "industry", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Industry", - "length": 0, - "no_copy": 0, "oldfieldname": "industry", "oldfieldtype": "Link", - "options": "Industry Type", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Industry Type" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "request_type", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Request Type", - "length": 0, - "no_copy": 0, "oldfieldname": "request_type", "oldfieldtype": "Select", - "options": "\nProduct Enquiry\nRequest for Information\nSuggestions\nOther", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "\nProduct Enquiry\nRequest for Information\nSuggestions\nOther" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "column_break3", "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "50%" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "company", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Company", - "length": 0, - "no_copy": 0, "oldfieldname": "company", "oldfieldtype": "Link", "options": "Company", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "remember_last_selected_value": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "fieldname": "website", + "fieldtype": "Data", + "label": "Website", + "oldfieldname": "website", + "oldfieldtype": "Data" + }, + { + "fieldname": "territory", + "fieldtype": "Link", + "label": "Territory", + "oldfieldname": "territory", + "oldfieldtype": "Link", + "options": "Territory", + "print_hide": 1 + }, + { + "default": "0", "fieldname": "unsubscribed", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Unsubscribed", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Unsubscribed" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "blog_subscriber", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Blog Subscriber", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Blog Subscriber" + }, + { + "fieldname": "title", + "fieldtype": "Data", + "hidden": 1, + "label": "Title", + "print_hide": 1 + }, + { + "fieldname": "designation", + "fieldtype": "Link", + "label": "Designation", + "options": "Designation" + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval: doc.__islocal", + "fieldname": "address_info", + "fieldtype": "Section Break", + "label": "Address & Contact", + "oldfieldtype": "Column Break", + "options": "fa fa-map-marker" + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval: doc.__islocal", + "fieldname": "contact_section", + "fieldtype": "Section Break", + "label": "Contact" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "fa fa-user", "idx": 5, "image_field": "image", - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-06-18 03:22:57.283628", + "links": [], + "modified": "2019-12-24 16:00:44.239168", "modified_by": "Administrator", "module": "CRM", "name": "Lead", @@ -1438,128 +447,69 @@ "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, "permlevel": 1, - "print": 0, "read": 1, "report": 1, - "role": "All", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "All" }, { - "amend": 0, - "cancel": 0, "create": 1, - "delete": 0, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Sales User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, "import": 1, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Sales Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, "create": 1, - "delete": 0, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "System Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, "permlevel": 1, - "print": 0, "read": 1, "report": 1, - "role": "Sales Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Sales Manager" }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, "permlevel": 1, - "print": 0, "read": 1, "report": 1, - "role": "Sales User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "role": "Sales User" + }, + { + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Guest", + "share": 1 } ], - "quick_entry": 0, - "read_only": 0, "search_fields": "lead_name,lead_owner,status", "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", - "title_field": "lead_name", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "title_field": "title" } \ No newline at end of file diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index 1dae4b9fc1c..73ef79b894a 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -2,18 +2,19 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -import frappe -from frappe import _ -from frappe.utils import (cstr, validate_email_address, cint, comma_and, has_gravatar, now, getdate, nowdate) -from frappe.model.mapper import get_mapped_doc -from erpnext.controllers.selling_controller import SellingController -from frappe.contacts.address_and_contact import load_address_and_contact +import frappe from erpnext.accounts.party import set_taxes +from erpnext.controllers.selling_controller import SellingController +from frappe import _ +from frappe.contacts.address_and_contact import load_address_and_contact from frappe.email.inbox import link_communication_to_document +from frappe.model.mapper import get_mapped_doc +from frappe.utils import cint, comma_and, cstr, getdate, has_gravatar, nowdate, validate_email_address sender_field = "email_id" + class Lead(SellingController): def get_feed(self): return '{0}: {1}'.format(_(self.status), self.lead_name) @@ -23,15 +24,23 @@ class Lead(SellingController): self.get("__onload").is_customer = customer load_address_and_contact(self) + def before_insert(self): + self.address_doc = self.create_address() + self.contact_doc = self.create_contact() + + def after_insert(self): + self.update_links() + # after the address and contact are created, flush the field values + # to avoid inconsistent reporting in case the documents are changed + self.flush_address_and_contact_fields() + def validate(self): self.set_lead_name() + self.set_title() self._prev = frappe._dict({ - "contact_date": frappe.db.get_value("Lead", self.name, "contact_date") if \ - (not cint(self.get("__islocal"))) else None, - "ends_on": frappe.db.get_value("Lead", self.name, "ends_on") if \ - (not cint(self.get("__islocal"))) else None, - "contact_by": frappe.db.get_value("Lead", self.name, "contact_by") if \ - (not cint(self.get("__islocal"))) else None, + "contact_date": frappe.db.get_value("Lead", self.name, "contact_date") if (not cint(self.is_new())) else None, + "ends_on": frappe.db.get_value("Lead", self.name, "ends_on") if (not cint(self.is_new())) else None, + "contact_by": frappe.db.get_value("Lead", self.name, "contact_by") if (not cint(self.is_new())) else None, }) self.set_status() @@ -39,7 +48,7 @@ class Lead(SellingController): if self.email_id: if not self.flags.ignore_email_validation: - validate_email_address(self.email_id, True) + validate_email_address(self.email_id, throw=True) if self.email_id == self.lead_owner: frappe.throw(_("Lead Owner cannot be same as the Lead")) @@ -53,8 +62,8 @@ class Lead(SellingController): if self.contact_date and getdate(self.contact_date) < getdate(nowdate()): frappe.throw(_("Next Contact Date cannot be in the past")) - if self.ends_on and self.contact_date and\ - (self.ends_on < self.contact_date): + if (self.ends_on and self.contact_date and + (getdate(self.ends_on) < getdate(self.contact_date))): frappe.throw(_("Ends On date cannot be before Next Contact Date.")) def on_update(self): @@ -66,23 +75,21 @@ class Lead(SellingController): "starts_on": self.contact_date, "ends_on": self.ends_on or "", "subject": ('Contact ' + cstr(self.lead_name)), - "description": ('Contact ' + cstr(self.lead_name)) + \ - (self.contact_by and ('. By : ' + cstr(self.contact_by)) or '') + "description": ('Contact ' + cstr(self.lead_name)) + (self.contact_by and ('. By : ' + cstr(self.contact_by)) or '') }, force) def check_email_id_is_unique(self): if self.email_id: # validate email is unique - duplicate_leads = frappe.db.sql_list("""select name from tabLead - where email_id=%s and name!=%s""", (self.email_id, self.name)) + duplicate_leads = frappe.get_all("Lead", filters={"email_id": self.email_id, "name": ["!=", self.name]}) + duplicate_leads = [lead.name for lead in duplicate_leads] if duplicate_leads: frappe.throw(_("Email Address must be unique, already exists for {0}") .format(comma_and(duplicate_leads)), frappe.DuplicateEntryError) def on_trash(self): - frappe.db.sql("""update `tabIssue` set lead='' where lead=%s""", - self.name) + frappe.db.sql("""update `tabIssue` set lead='' where lead=%s""", self.name) self.delete_events() @@ -115,10 +122,101 @@ class Lead(SellingController): self.lead_name = self.company_name + def set_title(self): + if self.organization_lead: + self.title = self.company_name + else: + self.title = self.lead_name + + def create_address(self): + address_fields = ["address_title", "address_line1", "address_line2", + "city", "county", "state", "country", "pincode"] + info_fields = ["email_id", "phone", "fax"] + + # do not create an address if no fields are available, + # skipping country since the system auto-sets it from system defaults + if not any([self.get(field) for field in address_fields if field != "country"]): + return + + address = frappe.new_doc("Address") + address.update({addr_field: self.get(addr_field) for addr_field in address_fields}) + address.update({info_field: self.get(info_field) for info_field in info_fields}) + address.insert() + + return address + + def create_contact(self): + if not self.lead_name: + self.set_lead_name() + + names = self.lead_name.split(" ") + if len(names) > 1: + first_name, last_name = names[0], " ".join(names[1:]) + else: + first_name, last_name = self.lead_name, None + + contact = frappe.new_doc("Contact") + contact.update({ + "first_name": first_name, + "last_name": last_name, + "salutation": self.salutation, + "gender": self.gender, + "designation": self.designation, + }) + + if self.email_id: + contact.append("email_ids", { + "email_id": self.email_id, + "is_primary": 1 + }) + + if self.phone: + contact.append("phone_nos", { + "phone": self.phone, + "is_primary": 1 + }) + + if self.mobile_no: + contact.append("phone_nos", { + "phone": self.mobile_no + }) + + contact.insert() + + return contact + + def update_links(self): + # update address links + if self.address_doc: + self.address_doc.append("links", { + "link_doctype": "Lead", + "link_name": self.name, + "link_title": self.lead_name + }) + self.address_doc.save() + + # update contact links + if self.contact_doc: + self.contact_doc.append("links", { + "link_doctype": "Lead", + "link_name": self.name, + "link_title": self.lead_name + }) + self.contact_doc.save() + + def flush_address_and_contact_fields(self): + fields = ['address_line1', 'address_line2', 'address_title', + 'city', 'county', 'country', 'fax', 'pincode', 'state'] + + for field in fields: + self.set(field, None) + + @frappe.whitelist() def make_customer(source_name, target_doc=None): return _make_customer(source_name, target_doc) + def _make_customer(source_name, target_doc=None, ignore_permissions=False): def set_missing_values(source, target): if source.company_name: @@ -143,6 +241,7 @@ def _make_customer(source_name, target_doc=None, ignore_permissions=False): return doclist + @frappe.whitelist() def make_opportunity(source_name, target_doc=None): def set_missing_values(source, target): @@ -164,6 +263,7 @@ def make_opportunity(source_name, target_doc=None): return target_doc + @frappe.whitelist() def make_quotation(source_name, target_doc=None): def set_missing_values(source, target): @@ -205,7 +305,8 @@ def _set_missing_values(source, target): @frappe.whitelist() def get_lead_details(lead, posting_date=None, company=None): - if not lead: return {} + if not lead: + return {} from erpnext.accounts.party import set_address_details out = frappe._dict() @@ -231,6 +332,7 @@ def get_lead_details(lead, posting_date=None, company=None): return out + @frappe.whitelist() def make_lead_from_communication(communication, ignore_communication_links=False): """ raise a issue from email """ @@ -267,4 +369,4 @@ def get_lead_with_phone_number(number): lead = leads[0].name if leads else None - return lead \ No newline at end of file + return lead diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index 66e3ca48dd2..0e2068a0a57 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -22,6 +22,7 @@ "sales_stage", "order_lost_reason", "mins_to_first_response", + "expected_closing", "next_contact", "contact_by", "contact_date", @@ -156,6 +157,11 @@ "label": "Mins to first response", "read_only": 1 }, + { + "fieldname": "expected_closing", + "fieldtype": "Date", + "label": "Expected Closing Date" + }, { "collapsible": 1, "collapsible_depends_on": "contact_by", diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 99486fa2066..5911db9f6c5 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -130,10 +130,10 @@ class Opportunity(TransactionBase): def has_lost_quotation(self): lost_quotation = frappe.db.sql(""" - select q.name - from `tabQuotation` q, `tabQuotation Item` qi - where q.name = qi.parent and q.docstatus=1 - and qi.prevdoc_docname =%s and q.status = 'Lost' + select name + from `tabQuotation` + where docstatus=1 + and opportunity =%s and status = 'Lost' """, self.name) if lost_quotation: if self.has_active_quotation(): @@ -307,7 +307,7 @@ def set_multiple_status(names, status): def auto_close_opportunity(): """ auto close the `Replied` Opportunities after 7 days """ - auto_close_after_days = frappe.db.get_value("Support Settings", "Support Settings", "close_opportunity_after_days") or 15 + auto_close_after_days = frappe.db.get_single_value("Selling Settings", "close_opportunity_after_days") or 15 opportunities = frappe.db.sql(""" select name from tabOpportunity where status='Replied' and modified { + frappe.db.get_value('Employee', {name: frm.doc.employee}, 'company', (d) => { frm.set_query("department", function() { return { "filters": { - "company": company, + "company": d.company, } }; }); @@ -16,7 +16,7 @@ frappe.ui.form.on("Instructor", { frm.set_query("department", "instructor_log", function() { return { "filters": { - "company": company, + "company": d.company, } }; }); diff --git a/erpnext/education/doctype/instructor/instructor.py b/erpnext/education/doctype/instructor/instructor.py index 0756b5f01a4..28df2fcdc11 100644 --- a/erpnext/education/doctype/instructor/instructor.py +++ b/erpnext/education/doctype/instructor/instructor.py @@ -22,3 +22,12 @@ class Instructor(Document): self.name = self.employee elif naming_method == 'Full Name': self.name = self.instructor_name + + def validate(self): + self.validate_duplicate_employee() + + def validate_duplicate_employee(self): + if self.employee and frappe.db.get_value("Instructor", {'employee': self.employee, 'name': ['!=', self.name]}, 'name'): + frappe.throw(_("Employee ID is linked with another instructor")) + + diff --git a/erpnext/education/doctype/program_enrollment/program_enrollment.py b/erpnext/education/doctype/program_enrollment/program_enrollment.py index d5348ffd067..75361728917 100644 --- a/erpnext/education/doctype/program_enrollment/program_enrollment.py +++ b/erpnext/education/doctype/program_enrollment/program_enrollment.py @@ -71,7 +71,7 @@ class ProgramEnrollment(Document): def create_course_enrollments(self): student = frappe.get_doc("Student", self.student) program = frappe.get_doc("Program", self.program) - course_list = [course.course for course in program.get_all_children()] + course_list = [course.course for course in program.courses] for course_name in course_list: student.enroll_in_course(course_name=course_name, program_enrollment=self.name) diff --git a/erpnext/education/doctype/question/question.py b/erpnext/education/doctype/question/question.py index 9a973c76154..a7deeab6f65 100644 --- a/erpnext/education/doctype/question/question.py +++ b/erpnext/education/doctype/question/question.py @@ -38,7 +38,7 @@ class Question(Document): options = self.options answers = [item.name for item in options if item.is_correct == True] if len(answers) == 0: - frappe.throw(_("No correct answer is set for {0}".format(self.name))) + frappe.throw(_("No correct answer is set for {0}").format(self.name)) return None elif len(answers) == 1: return answers[0] diff --git a/erpnext/education/doctype/student/student.js b/erpnext/education/doctype/student/student.js index b6e741c4da6..1936dcbd3e0 100644 --- a/erpnext/education/doctype/student/student.js +++ b/erpnext/education/doctype/student/student.js @@ -31,7 +31,7 @@ frappe.ui.form.on('Student', { frappe.ui.form.on('Student Guardian', { guardians_add: function(frm){ frm.fields_dict['guardians'].grid.get_field('guardian').get_query = function(doc){ - var guardian_list = []; + let guardian_list = []; if(!doc.__islocal) guardian_list.push(doc.guardian); $.each(doc.guardians, function(idx, val){ if (val.guardian) guardian_list.push(val.guardian); @@ -40,3 +40,18 @@ frappe.ui.form.on('Student Guardian', { }; } }); + + +frappe.ui.form.on('Student Sibling', { + siblings_add: function(frm){ + frm.fields_dict['siblings'].grid.get_field('student').get_query = function(doc){ + let sibling_list = [frm.doc.name]; + $.each(doc.siblings, function(idx, val){ + if (val.student && val.studying_in_same_institute == 'YES') { + sibling_list.push(val.student); + } + }); + return { filters: [['Student', 'name', 'not in', sibling_list]] }; + }; + } +}); \ No newline at end of file diff --git a/erpnext/education/doctype/student/student.py b/erpnext/education/doctype/student/student.py index 9af5e22913a..99c4c0e9089 100644 --- a/erpnext/education/doctype/student/student.py +++ b/erpnext/education/doctype/student/student.py @@ -5,12 +5,14 @@ from __future__ import unicode_literals import frappe from frappe.model.document import Document +from frappe.utils import getdate,today from frappe import _ from frappe.desk.form.linked_with import get_linked_doctypes from erpnext.education.utils import check_content_completion, check_quiz_completion class Student(Document): def validate(self): self.title = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name])) + self.validate_dates() if self.student_applicant: self.check_unique() @@ -19,6 +21,13 @@ class Student(Document): if frappe.get_value("Student", self.name, "title") != self.title: self.update_student_name_in_linked_doctype() + def validate_dates(self): + if self.date_of_birth and getdate(self.date_of_birth) >= getdate(today()): + frappe.throw(_("Date of Birth cannot be greater than today.")) + + if self.joining_date and self.date_of_leaving and getdate(self.joining_date) > getdate(self.date_of_leaving): + frappe.throw(_("Joining Date can not be greater than Leaving Date")) + def update_student_name_in_linked_doctype(self): linked_doctypes = get_linked_doctypes("Student") for d in linked_doctypes: @@ -40,7 +49,7 @@ class Student(Document): frappe.throw(_("Student {0} exist against student applicant {1}").format(student[0][0], self.student_applicant)) def after_insert(self): - if not frappe.get_single('Education Settings').user_creation_skip: + if not frappe.get_single('Education Settings').get('user_creation_skip'): self.create_student_user() def create_student_user(self): diff --git a/erpnext/education/doctype/student_group/student_group.js b/erpnext/education/doctype/student_group/student_group.js index c29c134843e..4165ce0f2e7 100644 --- a/erpnext/education/doctype/student_group/student_group.js +++ b/erpnext/education/doctype/student_group/student_group.js @@ -122,3 +122,15 @@ frappe.ui.form.on("Student Group", { } } }); + +frappe.ui.form.on('Student Group Instructor', { + instructors_add: function(frm){ + frm.fields_dict['instructors'].grid.get_field('instructor').get_query = function(doc){ + let instructor_list = []; + $.each(doc.instructors, function(idx, val){ + instructor_list.push(val.instructor); + }); + return { filters: [['Instructor', 'name', 'not in', instructor_list]] }; + }; + } +}); \ No newline at end of file diff --git a/erpnext/education/doctype/student_group/student_group.py b/erpnext/education/doctype/student_group/student_group.py index aba1b5ff5fd..aa542ddfbfd 100644 --- a/erpnext/education/doctype/student_group/student_group.py +++ b/erpnext/education/doctype/student_group/student_group.py @@ -34,15 +34,15 @@ class StudentGroup(Document): students = [d.student for d in program_enrollment] if program_enrollment else [] for d in self.students: if not frappe.db.get_value("Student", d.student, "enabled") and d.active and not self.disabled: - frappe.throw(_("{0} - {1} is inactive student".format(d.group_roll_number, d.student_name))) + frappe.throw(_("{0} - {1} is inactive student").format(d.group_roll_number, d.student_name)) if (self.group_based_on == "Batch") and cint(frappe.defaults.get_defaults().validate_batch)\ and d.student not in students: - frappe.throw(_("{0} - {1} is not enrolled in the Batch {2}".format(d.group_roll_number, d.student_name, self.batch))) + frappe.throw(_("{0} - {1} is not enrolled in the Batch {2}").format(d.group_roll_number, d.student_name, self.batch)) if (self.group_based_on == "Course") and cint(frappe.defaults.get_defaults().validate_course)\ and (d.student not in students): - frappe.throw(_("{0} - {1} is not enrolled in the Course {2}".format(d.group_roll_number, d.student_name, self.course))) + frappe.throw(_("{0} - {1} is not enrolled in the Course {2}").format(d.group_roll_number, d.student_name, self.course)) def validate_and_set_child_table_fields(self): roll_numbers = [d.group_roll_number for d in self.students if d.group_roll_number] @@ -55,7 +55,7 @@ class StudentGroup(Document): max_roll_no += 1 d.group_roll_number = max_roll_no if d.group_roll_number in roll_no_list: - frappe.throw(_("Duplicate roll number for student {0}".format(d.student_name))) + frappe.throw(_("Duplicate roll number for student {0}").format(d.student_name)) else: roll_no_list.append(d.group_roll_number) @@ -77,7 +77,7 @@ def get_students(academic_year, group_based_on, academic_term=None, program=None return [] def get_program_enrollment(academic_year, academic_term=None, program=None, batch=None, student_category=None, course=None): - + condition1 = " " condition2 = " " if academic_term: @@ -93,9 +93,9 @@ def get_program_enrollment(academic_year, academic_term=None, program=None, batc condition2 = ", `tabProgram Enrollment Course` pec" return frappe.db.sql(''' - select - pe.student, pe.student_name - from + select + pe.student, pe.student_name + from `tabProgram Enrollment` pe {condition2} where pe.academic_year = %(academic_year)s {condition1} diff --git a/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.py b/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.py index 643093ee485..d7645e30cdf 100644 --- a/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.py +++ b/erpnext/education/doctype/student_group_creation_tool/student_group_creation_tool.py @@ -74,4 +74,4 @@ class StudentGroupCreationTool(Document): student_group.append('students', student) student_group.save() - frappe.msgprint(_("{0} Student Groups created.".format(l))) \ No newline at end of file + frappe.msgprint(_("{0} Student Groups created.").format(l)) \ No newline at end of file diff --git a/erpnext/education/utils.py b/erpnext/education/utils.py index e0b278c2b1d..cffc3960a05 100644 --- a/erpnext/education/utils.py +++ b/erpnext/education/utils.py @@ -185,7 +185,7 @@ def add_activity(course, content_type, content, program): student = get_current_student() if not student: - return frappe.throw(_("Student with email {0} does not exist".format(frappe.session.user)), frappe.DoesNotExistError) + return frappe.throw(_("Student with email {0} does not exist").format(frappe.session.user), frappe.DoesNotExistError) enrollment = get_or_create_course_enrollment(course, program) if content_type == 'Quiz': @@ -220,7 +220,7 @@ def get_quiz(quiz_name, course): quiz = frappe.get_doc("Quiz", quiz_name) questions = quiz.get_questions() except: - frappe.throw(_("Quiz {0} does not exist".format(quiz_name))) + frappe.throw(_("Quiz {0} does not exist").format(quiz_name)) return None questions = [{ @@ -347,7 +347,7 @@ def get_or_create_course_enrollment(course, program): if not course_enrollment: program_enrollment = get_enrollment('program', program, student.name) if not program_enrollment: - frappe.throw(_("You are not enrolled in program {0}".format(program))) + frappe.throw(_("You are not enrolled in program {0}").format(program)) return return student.enroll_in_course(course_name=course, program_enrollment=get_enrollment('program', program, student.name)) else: diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py index 3be08a2757a..ca0e1609cb0 100644 --- a/erpnext/erpnext_integrations/connectors/shopify_connection.py +++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py @@ -259,6 +259,6 @@ def get_tax_account_head(tax): {"parent": "Shopify Settings", "shopify_tax": tax_title}, "tax_account") if not tax_account: - frappe.throw(_("Tax Account not specified for Shopify Tax {0}".format(tax.get("title")))) + frappe.throw(_("Tax Account not specified for Shopify Tax {0}").format(tax.get("title"))) return tax_account diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json index 9903048d0be..df77ad8bc93 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2018-10-25 10:02:48.656165", "doctype": "DocType", "editable_grid": 1, @@ -23,48 +24,41 @@ }, { "default": "0", - "depends_on": "eval:doc.enabled==1", + "depends_on": "enabled", "fieldname": "automatic_sync", "fieldtype": "Check", "label": "Synchronize all accounts every hour" }, { - "depends_on": "eval:doc.enabled==1", "fieldname": "plaid_client_id", "fieldtype": "Data", "in_list_view": 1, - "label": "Plaid Client ID", - "reqd": 1 + "label": "Plaid Client ID" }, { - "depends_on": "eval:doc.enabled==1", "fieldname": "plaid_secret", "fieldtype": "Password", "in_list_view": 1, - "label": "Plaid Secret", - "reqd": 1 + "label": "Plaid Secret" }, { - "depends_on": "eval:doc.enabled==1", "fieldname": "plaid_public_key", "fieldtype": "Data", "in_list_view": 1, - "label": "Plaid Public Key", - "reqd": 1 + "label": "Plaid Public Key" }, { - "depends_on": "eval:doc.enabled==1", "fieldname": "plaid_env", "fieldtype": "Data", "in_list_view": 1, - "label": "Plaid Environment", - "reqd": 1 + "label": "Plaid Environment" }, { "fieldname": "column_break_2", "fieldtype": "Column Break" }, { + "depends_on": "enabled", "fieldname": "section_break_4", "fieldtype": "Section Break" }, @@ -74,7 +68,8 @@ } ], "issingle": 1, - "modified": "2019-08-13 17:00:06.939422", + "links": [], + "modified": "2020-01-05 10:00:22.137832", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "Plaid Settings", diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index 4af1d740946..92111337fd2 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -53,13 +53,17 @@ def add_institution(token, response): @frappe.whitelist() def add_bank_accounts(response, bank, company): - response = json.loads(response) if not "accounts" in response else response + try: + response = json.loads(response) + except TypeError: + pass + bank = json.loads(bank) result = [] default_gl_account = get_default_bank_cash_account(company, "Bank") if not default_gl_account: - frappe.throw(_("Please setup a default bank account for company {0}".format(company))) + frappe.throw(_("Please setup a default bank account for company {0}").format(company)) for account in response["accounts"]: acc_type = frappe.db.get_value("Account Type", account["type"]) diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py index 0cad0cca72d..64c3b2d2730 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py @@ -50,7 +50,7 @@ class ShopifySettings(Document): deleted_webhooks = [] for d in self.webhooks: - url = get_shopify_url('admin/api/2019-04/webhooks.json'.format(d.webhook_id), self) + url = get_shopify_url('admin/api/2019-04/webhooks/{0}.json'.format(d.webhook_id), self) try: res = session.delete(url, headers=get_header(self)) res.raise_for_status() diff --git a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py index 141329b3db1..5a71ca5f154 100644 --- a/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py +++ b/erpnext/healthcare/doctype/clinical_procedure_template/clinical_procedure_template.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe, json from frappe import _ from frappe.model.document import Document +from frappe.model.rename_doc import rename_doc from frappe.utils import nowdate class ClinicalProcedureTemplate(Document): @@ -63,10 +64,11 @@ def updating_rate(self): item_code=%s""",(self.template, self.rate, self.item)) def create_item_from_template(doc): + disabled = 1 + if(doc.is_billable == 1): disabled = 0 - else: - disabled = 1 + #insert item item = frappe.get_doc({ "doctype": "Item", @@ -115,7 +117,7 @@ def change_item_code_from_template(item_code, doc): "item_code": item_code})): frappe.throw(_("Code {0} already exist").format(item_code)) else: - frappe.rename_doc("Item", doc.item_code, item_code, ignore_permissions = True) + rename_doc("Item", doc.item_code, item_code, ignore_permissions=True) frappe.db.set_value("Clinical Procedure Template", doc.name, "item_code", item_code) return diff --git a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py index 650499454b9..43f01c86b94 100644 --- a/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py +++ b/erpnext/healthcare/doctype/healthcare_service_unit_type/healthcare_service_unit_type.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document +from frappe.model.rename_doc import rename_doc class HealthcareServiceUnitType(Document): def validate(self): @@ -107,7 +108,7 @@ def change_item_code(item, item_code, doc_name): if(item_exist): frappe.throw(_("Code {0} already exist").format(item_code)) else: - frappe.rename_doc("Item", item, item_code, ignore_permissions = True) + rename_doc("Item", item, item_code, ignore_permissions=True) frappe.db.set_value("Healthcare Service Unit Type", doc_name, "item_code", item_code) @frappe.whitelist() diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json index 3bd2501a264..95d9e44cddb 100644 --- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json +++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json @@ -1,1368 +1,320 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, + "actions": [], "beta": 1, "creation": "2017-05-09 11:26:22.337760", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "sb_op_settings", + "patient_master_name", + "manage_customer", + "default_medical_code_standard", + "column_break_9", + "collect_registration_fee", + "registration_fee", + "manage_appointment_invoice_automatically", + "max_visit", + "valid_days", + "healthcare_service_items", + "inpatient_visit_charge_item", + "op_consulting_charge_item", + "column_break_13", + "clinical_procedure_consumable_item", + "out_patient_sms_alerts", + "reg_sms", + "reg_msg", + "app_con", + "app_con_msg", + "no_con", + "column_break_16", + "app_rem", + "app_rem_msg", + "rem_before", + "sb_in_ac", + "income_account", + "sb_r_ac", + "receivable_account", + "sb_lab_settings", + "create_test_on_si_submit", + "require_sample_collection", + "require_test_result_approval", + "column_break_34", + "employee_name_and_designation_in_print", + "custom_signature_in_print", + "laboratory_sms_alerts", + "sms_printed", + "column_break_28", + "sms_emailed" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "sb_op_settings", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Out Patient Settings", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Out Patient Settings" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "patient_master_name", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Patient Name By", - "length": 0, - "no_copy": 0, - "options": "Patient Name\nNaming Series", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Patient Name\nNaming Series" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "1", "description": "If checked, a customer will be created, mapped to Patient.\nPatient Invoices will be created against this Customer. You can also select existing Customer while creating Patient.", "fieldname": "manage_customer", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Manage Customer", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Manage Customer" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "default_medical_code_standard", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Default Medical Code Standard", - "length": 0, - "no_copy": 0, - "options": "Medical Code Standard", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Medical Code Standard" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_9", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "collect_registration_fee", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Collect Fee for Patient Registration", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Collect Fee for Patient Registration" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "collect_registration_fee", "fieldname": "registration_fee", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Registration Fee", - "length": 0, - "no_copy": 0, - "options": "Currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "mandatory_depends_on": "eval:doc.collect_registration_fee == 1", + "options": "Currency" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0", "description": "Manage Appointment Invoice submit and cancel automatically for Patient Encounter", "fieldname": "manage_appointment_invoice_automatically", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Invoice Appointments Automatically", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Invoice Appointments Automatically" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "max_visit", "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Patient Encounters in valid days", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Patient Encounters in valid days" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "valid_days", "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Valid number of days", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Valid number of days" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "healthcare_service_items", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Healthcare Service Items", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Healthcare Service Items" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "inpatient_visit_charge_item", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Inpatient Visit Charge Item", - "length": 0, - "no_copy": 0, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Item" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "op_consulting_charge_item", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Out Patient Consulting Charge Item", - "length": 0, - "no_copy": 0, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Item" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_13", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "clinical_procedure_consumable_item", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Clinical Procedure Consumable Item", - "length": 0, - "no_copy": 0, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Item" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "out_patient_sms_alerts", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Out Patient SMS Alerts", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Out Patient SMS Alerts" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "reg_sms", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Patient Registration", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Patient Registration" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Hello {{doc.patient}}, Thank you for registering with {{doc.company}}. Your ID is {{doc.id}} . Please note this ID for future reference. \nThank You, Get well soon!", "depends_on": "reg_sms", "fieldname": "reg_msg", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Registration Message", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Registration Message" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "app_con", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Appointment Confirmation", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Appointment Confirmation" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Hello {{doc.patient}}, You have scheduled an appointment with {{doc.practitioner}} by {{doc.start_dt}} at {{doc.company}}.\nThank you, Good day!", "depends_on": "app_con", "fieldname": "app_con_msg", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Confirmation Message", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Confirmation Message" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "depends_on": "app_con", "description": "Do not confirm if appointment is created for the same day", "fieldname": "no_con", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Avoid Confirmation", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Avoid Confirmation" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_16", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "app_rem", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Appointment Reminder", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Appointment Reminder" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Hello {{doc.patient}}, You have an appointment with {{doc.practitioner}} by {{doc.appointment_time}} at {{doc.company}}.\nThank you, Good day!\n", "depends_on": "app_rem", "fieldname": "app_rem_msg", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reminder Message", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Reminder Message" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "app_rem", "fieldname": "rem_before", "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Remind Before", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Remind Before" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "description": "Default income accounts to be used if not set in Healthcare Practitioner to book Appointment charges.", "fieldname": "sb_in_ac", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Income Account", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Income Account" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "income_account", "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Income Account", - "length": 0, - "no_copy": 0, - "options": "Party Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Party Account" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "description": "Default receivable accounts to be used if not set in Patient to book Appointment charges.", "fieldname": "sb_r_ac", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Receivable Account", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Receivable Account" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "receivable_account", "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Receivable Account", - "length": 0, - "no_copy": 0, - "options": "Party Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Party Account" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "sb_lab_settings", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Laboratory Settings", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Laboratory Settings" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "create_test_on_si_submit", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Create Lab Test(s) on Sales Invoice Submit", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Create Lab Test(s) on Sales Invoice Submit" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "description": "Create documents for sample collection", "fieldname": "require_sample_collection", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Manage Sample Collection", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Manage Sample Collection" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "require_test_result_approval", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Require Lab Test Approval", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Require Lab Test Approval" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_34", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "1", "fieldname": "employee_name_and_designation_in_print", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Employee name and designation in print", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Employee name and designation in print" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "eval:doc.employee_name_and_designation_in_print == '0'\n", "fieldname": "custom_signature_in_print", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Custom Signature in Print", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Custom Signature in Print" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "laboratory_sms_alerts", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Laboratory SMS Alerts", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Laboratory SMS Alerts" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Hello {{doc.patient}}, Your {{doc.lab_test_name}} result is ready with {{doc.company }}. \nThank You, Good day!", "fieldname": "sms_printed", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Result Printed", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Result Printed" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_28", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Hello {{doc.patient}}, Your {{doc.lab_test_name}} result has been emailed to {{doc.email}}. \n{{doc.company }}. \nThank You, Good day!", "fieldname": "sms_emailed", "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Result Emailed", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Result Emailed" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, "issingle": 1, - "istable": 0, - "max_attachments": 0, - "modified": "2018-10-09 22:03:37.449441", + "links": [], + "modified": "2020-01-23 13:31:43.699711", "modified_by": "Administrator", "module": "Healthcare", "name": "Healthcare Settings", - "name_case": "", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, - "report": 0, "role": "Healthcare Administrator", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 } ], "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, "restrict_to_domain": "Healthcare", - "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py index c107cd73350..835b38bedf8 100644 --- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py +++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import today, now_datetime +from frappe.utils import today, now_datetime, getdate from frappe.model.document import Document from frappe.desk.reportview import get_match_cond @@ -15,11 +15,20 @@ class InpatientRecord(Document): frappe.db.set_value("Patient", self.patient, "inpatient_record", self.name) def validate(self): + self.validate_dates() self.validate_already_scheduled_or_admitted() if self.status == "Discharged": frappe.db.set_value("Patient", self.patient, "inpatient_status", None) frappe.db.set_value("Patient", self.patient, "inpatient_record", None) + def validate_dates(self): + if (getdate(self.scheduled_date) < getdate(today())) or \ + (getdate(self.admitted_datetime) < getdate(today())): + frappe.throw(_("Scheduled and Admitted dates can not be less than today")) + if (getdate(self.expected_discharge) < getdate(self.scheduled_date)) or \ + (getdate(self.discharge_date) < getdate(self.scheduled_date)): + frappe.throw(_("Expected and Discharge dates cannot be less than Admission Schedule date")) + def validate_already_scheduled_or_admitted(self): query = """ select name, status diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.js b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.js index 3eb4f6abbbd..5c9bf49e608 100644 --- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.js +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.js @@ -3,17 +3,17 @@ frappe.ui.form.on("Lab Test Template",{ lab_test_name: function(frm) { - if(!frm.doc.lab_test_code) + if (!frm.doc.lab_test_code) frm.set_value("lab_test_code", frm.doc.lab_test_name); - if(!frm.doc.lab_test_description) + if (!frm.doc.lab_test_description) frm.set_value("lab_test_description", frm.doc.lab_test_name); }, - refresh : function(frm){ + refresh : function(frm) { // Restrict Special, Grouped type templates in Child TestGroups frm.set_query("lab_test_template", "lab_test_groups", function() { return { filters: { - lab_test_template_type:['in',['Single','Compound']] + lab_test_template_type: ['in',['Single','Compound']] } }; }); @@ -23,83 +23,44 @@ frappe.ui.form.on("Lab Test Template",{ cur_frm.cscript.custom_refresh = function(doc) { cur_frm.set_df_property("lab_test_code", "read_only", doc.__islocal ? 0 : 1); - if(!doc.__islocal) { - cur_frm.add_custom_button(__('Change Template Code'), function() { - change_template_code(cur_frm,doc); - } ); - if(doc.disabled == 1){ - cur_frm.add_custom_button(__('Enable Template'), function() { - enable_template(cur_frm); - } ); - } - else{ - cur_frm.add_custom_button(__('Disable Template'), function() { - disable_template(cur_frm); - } ); - } + if (!doc.__islocal) { + cur_frm.add_custom_button(__("Change Template Code"), function() { + change_template_code(doc); + }); } }; -var disable_template = function(frm){ - var doc = frm.doc; - frappe.call({ - method: "erpnext.healthcare.doctype.lab_test_template.lab_test_template.disable_enable_test_template", - args: {status: 1, name: doc.name, is_billable: doc.is_billable}, - callback: function(){ - cur_frm.reload_doc(); - } - }); -}; - -var enable_template = function(frm){ - var doc = frm.doc; - frappe.call({ - method: "erpnext.healthcare.doctype.lab_test_template.lab_test_template.disable_enable_test_template", - args: {status: 0, name: doc.name, is_billable: doc.is_billable}, - callback: function(){ - cur_frm.reload_doc(); - } - }); -}; - - -var change_template_code = function(frm,doc){ - var d = new frappe.ui.Dialog({ +let change_template_code = function(doc) { + let d = new frappe.ui.Dialog({ title:__("Change Template Code"), fields:[ { "fieldtype": "Data", - "label": "Test Template Code", - "fieldname": "Test Code", - reqd:1 - }, - { - "fieldtype": "Button", - "label": __("Change Code"), - click: function() { - var values = d.get_values(); - if(!values) - return; - change_test_code_from_template(values["Test Code"],doc); - d.hide(); - } + "label": "Lab Test Template Code", + "fieldname": "lab_test_code", + reqd: 1 } - ] + ], + primary_action: function() { + let values = d.get_values(); + if (values) { + frappe.call({ + "method": "erpnext.healthcare.doctype.lab_test_template.lab_test_template.change_test_code_from_template", + "args": {lab_test_code: values.lab_test_code, doc: doc}, + callback: function (data) { + frappe.set_route("Form", "Lab Test Template", data.message); + } + }); + } + d.hide(); + }, + primary_action_label: __("Change Template Code") }); d.show(); - d.set_values({ - 'Test Code': doc.lab_test_code - }); - var change_test_code_from_template = function(lab_test_code,doc){ - frappe.call({ - "method": "erpnext.healthcare.doctype.lab_test_template.lab_test_template.change_test_code_from_template", - "args": {lab_test_code: lab_test_code, doc: doc}, - callback: function (data) { - frappe.set_route("Form", "Lab Test Template", data.message); - } - }); - }; + d.set_values({ + "lab_test_code": doc.lab_test_code + }); }; frappe.ui.form.on("Lab Test Template", "lab_test_name", function(frm){ @@ -124,8 +85,8 @@ frappe.ui.form.on("Lab Test Template", "lab_test_description", function(frm){ }); frappe.ui.form.on("Lab Test Groups", "template_or_new_line", function (frm, cdt, cdn) { - var child = locals[cdt][cdn]; - if(child.template_or_new_line =="Add new line"){ + let child = locals[cdt][cdn]; + if (child.template_or_new_line == "Add new line") { frappe.model.set_value(cdt, cdn, 'lab_test_template', ""); frappe.model.set_value(cdt, cdn, 'lab_test_description', ""); } diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json index a707560f650..fcfecba5767 100644 --- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json @@ -1,1036 +1,276 @@ { - "allow_copy": 1, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:lab_test_code", - "beta": 1, - "creation": "2016-03-29 17:35:36.761223", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 0, + "actions": [], + "allow_copy": 1, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:lab_test_code", + "beta": 1, + "creation": "2016-03-29 17:35:36.761223", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "lab_test_name", + "item", + "lab_test_code", + "lab_test_group", + "department", + "column_break_3", + "lab_test_template_type", + "disabled", + "is_billable", + "lab_test_rate", + "section_break_normal", + "lab_test_uom", + "lab_test_normal_range", + "column_break_10", + "section_break_compound", + "normal_test_templates", + "section_break_special", + "sensitivity", + "special_test_template", + "section_break_group", + "lab_test_groups", + "section_break_description", + "lab_test_description", + "sb_sample_collection", + "sample", + "sample_uom", + "sample_quantity", + "sample_collection_details", + "change_in_item" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lab_test_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Test Name", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "lab_test_name", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Test Name", + "no_copy": 1, + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item", - "length": 0, - "no_copy": 1, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item", + "fieldtype": "Link", + "label": "Item", + "no_copy": 1, + "options": "Item", + "read_only": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lab_test_code", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Item Code", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "fieldname": "lab_test_code", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Item Code", + "no_copy": 1, + "reqd": 1, "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "lab_test_group", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Item Group", - "length": 0, - "no_copy": 0, - "options": "Item Group", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "lab_test_group", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_standard_filter": 1, + "label": "Item Group", + "options": "Item Group", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Medical Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "department", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_standard_filter": 1, + "label": "Department", + "options": "Medical Department", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "description": "Single for results which require only a single input, result UOM and normal value \n
\nCompound for results which require multiple input fields with corresponding event names, result UOMs and normal values\n
\nDescriptive for tests which have multiple result components and corresponding result entry fields. \n
\nGrouped for test templates which are a group of other test templates.\n
\nNo Result for tests with no results. Also, no Lab Test is created. e.g.. Sub Tests for Grouped results.", - "fieldname": "lab_test_template_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Result Format", - "length": 0, - "no_copy": 0, - "options": "\nSingle\nCompound\nDescriptive\nGrouped\nNo Result", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "Single for results which require only a single input, result UOM and normal value \n
\nCompound for results which require multiple input fields with corresponding event names, result UOMs and normal values\n
\nDescriptive for tests which have multiple result components and corresponding result entry fields. \n
\nGrouped for test templates which are a group of other test templates.\n
\nNo Result for tests with no results. Also, no Lab Test is created. e.g.. Sub Tests for Grouped results.", + "fieldname": "lab_test_template_type", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Result Format", + "options": "\nSingle\nCompound\nDescriptive\nGrouped\nNo Result" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "depends_on": "eval:doc.lab_test_template_type != 'Grouped'", - "description": "If unchecked, the item wont be appear in Sales Invoice, but can be used in group test creation. ", - "fieldname": "is_billable", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is billable", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "1", + "depends_on": "eval:doc.lab_test_template_type != 'Grouped'", + "description": "If unchecked, the item wont be appear in Sales Invoice, but can be used in group test creation. ", + "fieldname": "is_billable", + "fieldtype": "Check", + "label": "Is billable", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_billable == 1", - "description": "This value is updated in the Default Sales Price List.", - "fieldname": "lab_test_rate", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Standard Selling Rate", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.is_billable == 1", + "description": "This value is updated in the Default Sales Price List.", + "fieldname": "lab_test_rate", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Standard Selling Rate" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": "", - "columns": 0, - "depends_on": "eval:doc.lab_test_template_type == 'Single'", - "fieldname": "section_break_normal", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Lab Routine", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.lab_test_template_type == 'Single'", + "fieldname": "section_break_normal", + "fieldtype": "Section Break", + "label": "Lab Routine" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lab_test_uom", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "UOM", - "length": 0, - "no_copy": 0, - "options": "Lab Test UOM", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "lab_test_uom", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "label": "UOM", + "options": "Lab Test UOM" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lab_test_normal_range", - "fieldtype": "Long Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Normal Range", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "lab_test_normal_range", + "fieldtype": "Long Text", + "ignore_xss_filter": 1, + "label": "Normal Range" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_10", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": "", - "columns": 0, - "depends_on": "eval:doc.lab_test_template_type == 'Compound'", - "fieldname": "section_break_compound", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Compound", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.lab_test_template_type == 'Compound'", + "fieldname": "section_break_compound", + "fieldtype": "Section Break", + "label": "Compound" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "normal_test_templates", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "options": "Normal Test Template", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "normal_test_templates", + "fieldtype": "Table", + "options": "Normal Test Template" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.lab_test_template_type == 'Descriptive'", - "fieldname": "section_break_special", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Special", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.lab_test_template_type == 'Descriptive'", + "fieldname": "section_break_special", + "fieldtype": "Section Break", + "label": "Special" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "sensitivity", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sensitivity", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "sensitivity", + "fieldtype": "Check", + "label": "Sensitivity" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "special_test_template", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "options": "Special Test Template", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "special_test_template", + "fieldtype": "Table", + "options": "Special Test Template" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.lab_test_template_type == 'Grouped'", - "fieldname": "section_break_group", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Group", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.lab_test_template_type == 'Grouped'", + "fieldname": "section_break_group", + "fieldtype": "Section Break", + "label": "Group" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lab_test_groups", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "options": "Lab Test Groups", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "lab_test_groups", + "fieldtype": "Table", + "options": "Lab Test Groups" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_description", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_description", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "lab_test_description", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "lab_test_description", + "fieldtype": "Text", + "ignore_xss_filter": 1, + "label": "Description", + "no_copy": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sb_sample_collection", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sample Collection", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sb_sample_collection", + "fieldtype": "Section Break", + "label": "Sample Collection" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sample", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sample", - "length": 0, - "no_copy": 0, - "options": "Lab Test Sample", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sample", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Sample", + "options": "Lab Test Sample" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "sample.sample_uom", - "fieldname": "sample_uom", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "UOM", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "sample.sample_uom", + "fieldname": "sample_uom", + "fieldtype": "Data", + "label": "UOM", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "sample_quantity", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Quantity", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "sample_quantity", + "fieldtype": "Float", + "label": "Quantity" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sample_collection_details", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 1, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Collection Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sample_collection_details", + "fieldtype": "Text", + "ignore_xss_filter": 1, + "label": "Collection Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "change_in_item", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Change In Item", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "change_in_item", + "fieldtype": "Check", + "hidden": 1, + "label": "Change In Item", + "no_copy": 1, + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "disabled", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "disabled", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-09-04 11:16:02.349707", - "modified_by": "Administrator", - "module": "Healthcare", - "name": "Lab Test Template", - "name_case": "", - "owner": "Administrator", + ], + "links": [], + "modified": "2020-01-21 21:02:16.108347", + "modified_by": "ruchamahabal2@gmail.com", + "module": "Healthcare", + "name": "Lab Test Template", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Healthcare Administrator", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Healthcare Administrator", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Laboratory User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 0 + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Laboratory User", + "share": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Healthcare", - "search_fields": "lab_test_code,lab_test_name,lab_test_template_type", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "lab_test_name", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "restrict_to_domain": "Healthcare", + "search_fields": "lab_test_code,lab_test_name,lab_test_template_type", + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "lab_test_name", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py index 101e143c123..fd7ae801d97 100644 --- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py @@ -5,62 +5,80 @@ from __future__ import unicode_literals import frappe, json from frappe.model.document import Document +from frappe.model.rename_doc import rename_doc from frappe import _ class LabTestTemplate(Document): + def after_insert(self): + if not self.item: + create_item_from_template(self) + + def validate(self): + self.enable_disable_item() + def on_update(self): - #Item and Price List update --> if (change_in_item) - if(self.change_in_item and self.is_billable == 1 and self.item): - updating_item(self) - item_price = item_price_exist(self) + # if change_in_item update Item and Price List + if self.change_in_item and self.is_billable and self.item: + self.update_item() + item_price = self.item_price_exists() if not item_price: - if(self.lab_test_rate != 0.0): + if self.lab_test_rate != 0.0: price_list_name = frappe.db.get_value("Price List", {"selling": 1}) - if(self.lab_test_rate): + if self.lab_test_rate: make_item_price(self.lab_test_code, price_list_name, self.lab_test_rate) else: make_item_price(self.lab_test_code, price_list_name, 0.0) else: frappe.db.set_value("Item Price", item_price, "price_list_rate", self.lab_test_rate) - frappe.db.set_value(self.doctype,self.name,"change_in_item",0) - elif(self.is_billable == 0 and self.item): - frappe.db.set_value("Item",self.item,"disabled",1) + frappe.db.set_value(self.doctype, self.name, "change_in_item", 0) + + elif not self.is_billable and self.item: + frappe.db.set_value("Item", self.item, "disabled", 1) + self.reload() - def after_insert(self): - if not self.item: - create_item_from_template(self) - - #Call before delete the template def on_trash(self): - # remove template refernce from item and disable item - if(self.item): + # remove template reference from item and disable item + if self.item: try: - frappe.delete_doc("Item",self.item, force=True) + frappe.delete_doc("Item", self.item) except Exception: - frappe.throw(_("""Not permitted. Please disable the Test Template""")) + frappe.throw(_("Not permitted. Please disable the Lab Test Template")) -def item_price_exist(doc): - item_price = frappe.db.exists({ - "doctype": "Item Price", - "item_code": doc.lab_test_code}) - if(item_price): - return item_price[0][0] - else: - return False + def enable_disable_item(self): + if self.is_billable: + if self.disabled: + frappe.db.set_value('Item', self.item, 'disabled', 1) + else: + frappe.db.set_value('Item', self.item, 'disabled', 0) + + def update_item(self): + item = frappe.get_doc("Item", self.item) + if item: + item.update({ + "item_name": self.lab_test_name, + "item_group": self.lab_test_group, + "disabled": 0, + "standard_rate": self.lab_test_rate, + "description": self.lab_test_description + }) + item.save() + + def item_price_exists(self): + item_price = frappe.db.exists({"doctype": "Item Price", "item_code": self.lab_test_code}) + if item_price: + return item_price[0][0] + else: + return False -def updating_item(self): - frappe.db.sql("""update `tabItem` set item_name=%s, item_group=%s, disabled=0, standard_rate=%s, - description=%s, modified=NOW() where item_code=%s""", - (self.lab_test_name, self.lab_test_group , self.lab_test_rate, self.lab_test_description, self.item)) def create_item_from_template(doc): - if(doc.is_billable == 1): + if doc.is_billable: disabled = 0 else: disabled = 1 - #insert item + # insert item item = frappe.get_doc({ "doctype": "Item", "item_code": doc.lab_test_code, @@ -77,9 +95,9 @@ def create_item_from_template(doc): "stock_uom": "Unit" }).insert(ignore_permissions=True) - #insert item price - #get item price list to insert item price - if(doc.lab_test_rate != 0.0): + # insert item price + # get item price list to insert item price + if doc.lab_test_rate != 0.0: price_list_name = frappe.db.get_value("Price List", {"selling": 1}) if(doc.lab_test_rate): make_item_price(item.name, price_list_name, doc.lab_test_rate) @@ -88,10 +106,10 @@ def create_item_from_template(doc): make_item_price(item.name, price_list_name, 0.0) item.standard_rate = 0.0 item.save(ignore_permissions = True) - #Set item to the template + # Set item in the template frappe.db.set_value("Lab Test Template", doc.name, "item", item.name) - doc.reload() #refresh the doc after insert. + doc.reload() def make_item_price(item, price_list_name, item_price): frappe.get_doc({ @@ -103,22 +121,13 @@ def make_item_price(item, price_list_name, item_price): @frappe.whitelist() def change_test_code_from_template(lab_test_code, doc): - args = json.loads(doc) - doc = frappe._dict(args) + doc = frappe._dict(json.loads(doc)) - item_exist = frappe.db.exists({ - "doctype": "Item", - "item_code": lab_test_code}) - if(item_exist): - frappe.throw(_("Code {0} already exist").format(lab_test_code)) + if frappe.db.exists({ "doctype": "Item", "item_code": lab_test_code}): + frappe.throw(_("Lab Test Item {0} already exist").format(lab_test_code)) else: - frappe.rename_doc("Item", doc.name, lab_test_code, ignore_permissions = True) - frappe.db.set_value("Lab Test Template",doc.name,"lab_test_code",lab_test_code) - frappe.rename_doc("Lab Test Template", doc.name, lab_test_code, ignore_permissions = True) - return lab_test_code - -@frappe.whitelist() -def disable_enable_test_template(status, name, is_billable): - frappe.db.set_value("Lab Test Template",name,"disabled",status) - if(is_billable == 1): - frappe.db.set_value("Item",name,"disabled",status) + rename_doc("Item", doc.name, lab_test_code, ignore_permissions=True) + frappe.db.set_value("Lab Test Template", doc.name, "lab_test_code", lab_test_code) + frappe.db.set_value("Lab Test Template", doc.name, "lab_test_name", lab_test_code) + rename_doc("Lab Test Template", doc.name, lab_test_code, ignore_permissions=True) + return lab_test_code \ No newline at end of file diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js b/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js index a16a72f55b2..a86075fc943 100644 --- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js +++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js @@ -3,13 +3,5 @@ */ frappe.listview_settings['Lab Test Template'] = { add_fields: ["lab_test_name", "lab_test_code", "lab_test_rate"], - filters:[["disabled","=",0]], - /* get_indicator: function(doc) { - if(doc.disabled==1){ - return [__("Disabled"), "red", "disabled,=,Disabled"]; - } - if(doc.disabled==0){ - return [__("Enabled"), "green", "disabled,=,0"]; - } - } */ + filters: [["disabled", "=", 0]] }; diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py index 7aa41c546c0..d5622454eb3 100755 --- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py +++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py @@ -102,7 +102,7 @@ def invoice_appointment(appointment_doc): sales_invoice.save(ignore_permissions=True) sales_invoice.submit() - frappe.msgprint(_("Sales Invoice {0} created as paid".format(sales_invoice.name)), alert=True) + frappe.msgprint(_("Sales Invoice {0} created as paid").format(sales_invoice.name), alert=True) def appointment_cancel(appointment_id): appointment = frappe.get_doc("Patient Appointment", appointment_id) @@ -111,7 +111,7 @@ def appointment_cancel(appointment_id): sales_invoice = exists_sales_invoice(appointment) if sales_invoice and cancel_sales_invoice(sales_invoice): frappe.msgprint( - _("Appointment {0} and Sales Invoice {1} cancelled".format(appointment.name, sales_invoice.name)) + _("Appointment {0} and Sales Invoice {1} cancelled").format(appointment.name, sales_invoice.name) ) else: validity = validity_exists(appointment.practitioner, appointment.patient) @@ -121,7 +121,7 @@ def appointment_cancel(appointment_id): visited = fee_validity.visited - 1 frappe.db.set_value("Fee Validity", fee_validity.name, "visited", visited) frappe.msgprint( - _("Appointment cancelled, Please review and cancel the invoice {0}".format(fee_validity.ref_invoice)) + _("Appointment cancelled, Please review and cancel the invoice {0}").format(fee_validity.ref_invoice) ) else: frappe.msgprint(_("Appointment cancelled")) @@ -203,7 +203,7 @@ def get_availability_data(date, practitioner): if employee: # Check if it is Holiday if is_holiday(employee, date): - frappe.throw(_("{0} is a company holiday".format(date))) + frappe.throw(_("{0} is a company holiday").format(date)) # Check if He/She on Leave leave_record = frappe.db.sql("""select half_day from `tabLeave Application` @@ -221,7 +221,7 @@ def get_availability_data(date, practitioner): if schedule.schedule: practitioner_schedule = frappe.get_doc("Practitioner Schedule", schedule.schedule) else: - frappe.throw(_("{0} does not have a Healthcare Practitioner Schedule. Add it in Healthcare Practitioner master".format(practitioner))) + frappe.throw(_("{0} does not have a Healthcare Practitioner Schedule. Add it in Healthcare Practitioner master").format(practitioner)) if practitioner_schedule: available_slots = [] @@ -259,7 +259,7 @@ def get_availability_data(date, practitioner): "avail_slot":available_slots, 'appointments': appointments}) else: - frappe.throw(_("{0} does not have a Healthcare Practitioner Schedule. Add it in Healthcare Practitioner master".format(practitioner))) + frappe.throw(_("{0} does not have a Healthcare Practitioner Schedule. Add it in Healthcare Practitioner master").format(practitioner)) if not available_slots and not slot_details: # TODO: return available slots in nearby dates diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 9e74bfd2906..774c917f682 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -40,8 +40,6 @@ after_install = "erpnext.setup.install.after_install" boot_session = "erpnext.startup.boot.boot_session" notification_config = "erpnext.startup.notifications.get_notification_config" get_help_messages = "erpnext.utilities.activation.get_help_messages" -get_user_progress_slides = "erpnext.utilities.user_progress.get_user_progress_slides" -update_and_get_user_progress = "erpnext.utilities.user_progress_utils.update_default_domain_actions_and_get_state" leaderboards = "erpnext.startup.leaderboard.get_leaderboards" @@ -182,6 +180,7 @@ standard_portal_menu_items = [ {"title": _("Admission"), "route": "/admissions", "reference_doctype": "Student Admission", "role": "Student"}, {"title": _("Certification"), "route": "/certification", "reference_doctype": "Certification Application", "role": "Non Profit Portal User"}, {"title": _("Material Request"), "route": "/material-requests", "reference_doctype": "Material Request", "role": "Customer"}, + {"title": _("Appointment Booking"), "route": "/book_appointment"}, ] default_roles = [ @@ -248,10 +247,10 @@ doc_events = { "on_trash": "erpnext.regional.check_deletion_permission" }, 'Address': { - 'validate': ['erpnext.regional.india.utils.validate_gstin_for_india', 'erpnext.regional.italy.utils.set_state_code'] + 'validate': ['erpnext.regional.india.utils.validate_gstin_for_india', 'erpnext.regional.italy.utils.set_state_code', 'erpnext.regional.india.utils.update_gst_category'] }, - ('Sales Invoice', 'Purchase Invoice', 'Delivery Note'): { - 'validate': 'erpnext.regional.india.utils.set_place_of_supply' + ('Sales Invoice', 'Sales Order', 'Delivery Note', 'Purchase Invoice', 'Purchase Order', 'Purchase Receipt'): { + 'validate': ['erpnext.regional.india.utils.set_place_of_supply'] }, "Contact": { "on_trash": "erpnext.support.doctype.issue.issue.update_issue", @@ -265,6 +264,13 @@ doc_events = { } } +# On cancel event Payment Entry will be exempted and all linked submittable doctype will get cancelled. +# to maintain data integrity we exempted payment entry. it will un-link when sales invoice get cancelled. +# if payment entry not in auto cancel exempted doctypes it will cancel payment entry. +auto_cancel_exempted_doctypes= [ + "Payment Entry" +] + scheduler_events = { "all": [ "erpnext.projects.doctype.project.project.project_status_update_reminder" @@ -301,7 +307,8 @@ scheduler_events = { "erpnext.quality_management.doctype.quality_review.quality_review.review", "erpnext.support.doctype.service_level_agreement.service_level_agreement.check_agreement_status", "erpnext.crm.doctype.email_campaign.email_campaign.send_email_to_leads_or_contacts", - "erpnext.crm.doctype.email_campaign.email_campaign.set_email_campaign_status" + "erpnext.crm.doctype.email_campaign.email_campaign.set_email_campaign_status", + "erpnext.selling.doctype.quotation.quotation.set_expired_status" ], "daily_long": [ "erpnext.setup.doctype.email_digest.email_digest.send", diff --git a/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation.py b/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation.py index 6f9e4a9799d..a8ebe8610ec 100644 --- a/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation.py +++ b/erpnext/hotels/doctype/hotel_room_reservation/hotel_room_reservation.py @@ -32,8 +32,8 @@ class HotelRoomReservation(Document): + d.qty + self.rooms_booked.get(d.item) total_rooms = self.get_total_rooms(d.item) if total_rooms < rooms_booked: - frappe.throw(_("Hotel Rooms of type {0} are unavailable on {1}".format(d.item, - frappe.format(day, dict(fieldtype="Date")))), exc=HotelRoomUnavailableError) + frappe.throw(_("Hotel Rooms of type {0} are unavailable on {1}").format(d.item, + frappe.format(day, dict(fieldtype="Date"))), exc=HotelRoomUnavailableError) self.rooms_booked[d.item] += rooms_booked @@ -74,8 +74,8 @@ class HotelRoomReservation(Document): net_rate += day_rate[0][0] else: frappe.throw( - _("Please set Hotel Room Rate on {}".format( - frappe.format(day, dict(fieldtype="Date")))), exc=HotelRoomPricingNotSetError) + _("Please set Hotel Room Rate on {}").format( + frappe.format(day, dict(fieldtype="Date"))), exc=HotelRoomPricingNotSetError) d.rate = net_rate d.amount = net_rate * flt(d.qty) self.net_total += d.amount diff --git a/erpnext/hr/doctype/additional_salary/additional_salary.json b/erpnext/hr/doctype/additional_salary/additional_salary.json index 6768b49e288..7d69f7e7fc2 100644 --- a/erpnext/hr/doctype/additional_salary/additional_salary.json +++ b/erpnext/hr/doctype/additional_salary/additional_salary.json @@ -1,560 +1,173 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2018-05-10 12:04:08.396461", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2018-05-10 12:04:08.396461", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "naming_series", + "employee", + "employee_name", + "salary_component", + "overwrite_salary_structure_amount", + "deduct_full_tax_on_selected_payroll_date", + "column_break_5", + "company", + "payroll_date", + "salary_slip", + "type", + "department", + "amount", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fetch_if_empty": 0, - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Series", - "length": 0, - "no_copy": 0, - "options": "HR-ADS-.YY.-.MM.-", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "HR-ADS-.YY.-.MM.-", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "salary_component", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Salary Component", - "length": 0, - "no_copy": 0, - "options": "Salary Component", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "salary_component", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Salary Component", + "options": "Salary Component", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "fetch_if_empty": 0, - "fieldname": "overwrite_salary_structure_amount", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Overwrite Salary Structure Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "1", + "fieldname": "overwrite_salary_structure_amount", + "fieldtype": "Check", + "label": "Overwrite Salary Structure Amount" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "deduct_full_tax_on_selected_payroll_date", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Deduct Full Tax on Selected Payroll Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "deduct_full_tax_on_selected_payroll_date", + "fieldtype": "Check", + "label": "Deduct Full Tax on Selected Payroll Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_5", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "description": "Date on which this component is applied", - "fetch_if_empty": 0, - "fieldname": "payroll_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Payroll Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "Date on which this component is applied", + "fieldname": "payroll_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Payroll Date", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.employee_name", - "fetch_if_empty": 0, - "fieldname": "employee_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Employee Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.department", - "fetch_if_empty": 0, - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "salary_slip", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Salary Slip", - "length": 0, - "no_copy": 0, - "options": "Salary Slip", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "salary_slip", + "fieldtype": "Link", + "label": "Salary Slip", + "options": "Salary Slip", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "salary_component.type", - "fetch_if_empty": 0, - "fieldname": "type", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Salary Component Type", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "salary_component.type", + "fieldname": "type", + "fieldtype": "Data", + "label": "Salary Component Type", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Additional Salary", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Additional Salary", + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-05-09 19:53:37.475839", - "modified_by": "Administrator", - "module": "HR", - "name": "Additional Salary", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2019-12-12 19:07:23.635901", + "modified_by": "Administrator", + "module": "HR", + "name": "Additional Salary", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "employee", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "employee", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/hr/doctype/appointment_letter/__init__.py b/erpnext/hr/doctype/appointment_letter/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/doctype/appointment_letter/appointment_letter.js b/erpnext/hr/doctype/appointment_letter/appointment_letter.js new file mode 100644 index 00000000000..a338dc65136 --- /dev/null +++ b/erpnext/hr/doctype/appointment_letter/appointment_letter.js @@ -0,0 +1,30 @@ +// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Appointment Letter', { + appointment_letter_template: function(frm){ + if (frm.doc.appointment_letter_template){ + frappe.call({ + method: 'erpnext.hr.doctype.appointment_letter.appointment_letter.get_appointment_letter_details', + args : { + template : frm.doc.appointment_letter_template + }, + callback: function(r){ + if(r.message){ + let message_body = r.message; + frm.set_value("introduction", message_body[0].introduction); + frm.set_value("closing_notes", message_body[0].closing_notes); + frm.doc.terms = [] + for (var i in message_body[1].description){ + frm.add_child("terms"); + frm.fields_dict.terms.get_value()[i].title = message_body[1].description[i].title; + frm.fields_dict.terms.get_value()[i].description = message_body[1].description[i].description; + } + frm.refresh(); + } + } + + }); + } + }, +}); diff --git a/erpnext/hr/doctype/appointment_letter/appointment_letter.json b/erpnext/hr/doctype/appointment_letter/appointment_letter.json new file mode 100644 index 00000000000..c81b7004f63 --- /dev/null +++ b/erpnext/hr/doctype/appointment_letter/appointment_letter.json @@ -0,0 +1,124 @@ +{ + "actions": [], + "autoname": "HR-APP-LETTER-.#####", + "creation": "2019-12-26 12:35:49.574828", + "default_print_format": "Standard Appointment Letter", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "job_applicant", + "applicant_name", + "column_break_3", + "company", + "appointment_date", + "appointment_letter_template", + "body_section", + "introduction", + "terms", + "closing_notes" + ], + "fields": [ + { + "fetch_from": "job_applicant.applicant_name", + "fieldname": "applicant_name", + "fieldtype": "Data", + "in_global_search": 1, + "in_list_view": 1, + "label": "Applicant Name", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "appointment_date", + "fieldtype": "Date", + "label": "Appointment Date", + "reqd": 1 + }, + { + "fieldname": "appointment_letter_template", + "fieldtype": "Link", + "label": "Appointment Letter Template", + "options": "Appointment Letter Template", + "reqd": 1 + }, + { + "fetch_from": "appointment_letter_template.introduction", + "fieldname": "introduction", + "fieldtype": "Long Text", + "label": "Introduction", + "reqd": 1 + }, + { + "fieldname": "body_section", + "fieldtype": "Section Break", + "label": "Body" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "job_applicant", + "fieldtype": "Link", + "label": "Job Applicant", + "options": "Job Applicant", + "reqd": 1 + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "fieldname": "closing_notes", + "fieldtype": "Text", + "label": "Closing Notes" + }, + { + "fieldname": "terms", + "fieldtype": "Table", + "label": "Terms", + "options": "Appointment Letter content", + "reqd": 1 + } + ], + "links": [], + "modified": "2020-01-21 17:30:36.334395", + "modified_by": "Administrator", + "module": "HR", + "name": "Appointment Letter", + "name_case": "Title Case", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/appointment_letter/appointment_letter.py b/erpnext/hr/doctype/appointment_letter/appointment_letter.py new file mode 100644 index 00000000000..85b82c50145 --- /dev/null +++ b/erpnext/hr/doctype/appointment_letter/appointment_letter.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class AppointmentLetter(Document): + pass + +@frappe.whitelist() +def get_appointment_letter_details(template): + body = [] + intro= frappe.get_list("Appointment Letter Template", + fields = ['introduction', 'closing_notes'], + filters={'name': template + })[0] + content = frappe.get_list("Appointment Letter content", + fields = ['title', 'description'], + filters={'parent': template + }) + body.append(intro) + body.append({'description': content}) + return body diff --git a/erpnext/hr/doctype/appointment_letter/test_appointment_letter.py b/erpnext/hr/doctype/appointment_letter/test_appointment_letter.py new file mode 100644 index 00000000000..b9ce9819c5a --- /dev/null +++ b/erpnext/hr/doctype/appointment_letter/test_appointment_letter.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestAppointmentLetter(unittest.TestCase): + pass diff --git a/erpnext/hr/doctype/appointment_letter_content/__init__.py b/erpnext/hr/doctype/appointment_letter_content/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/doctype/appointment_letter_content/appointment_letter_content.json b/erpnext/hr/doctype/appointment_letter_content/appointment_letter_content.json new file mode 100644 index 00000000000..17a2b91cffe --- /dev/null +++ b/erpnext/hr/doctype/appointment_letter_content/appointment_letter_content.json @@ -0,0 +1,39 @@ +{ + "actions": [], + "creation": "2019-12-26 12:22:16.575767", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "title", + "description" + ], + "fields": [ + { + "fieldname": "title", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Title", + "reqd": 1 + }, + { + "fieldname": "description", + "fieldtype": "Long Text", + "in_list_view": 1, + "label": "Description", + "reqd": 1 + } + ], + "istable": 1, + "links": [], + "modified": "2019-12-26 12:24:09.824084", + "modified_by": "Administrator", + "module": "HR", + "name": "Appointment Letter content", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/appointment_letter_content/appointment_letter_content.py b/erpnext/hr/doctype/appointment_letter_content/appointment_letter_content.py new file mode 100644 index 00000000000..a1a49e536b6 --- /dev/null +++ b/erpnext/hr/doctype/appointment_letter_content/appointment_letter_content.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class AppointmentLettercontent(Document): + pass diff --git a/erpnext/hr/doctype/appointment_letter_template/__init__.py b/erpnext/hr/doctype/appointment_letter_template/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.js b/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.js new file mode 100644 index 00000000000..8270f7aca2c --- /dev/null +++ b/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.js @@ -0,0 +1,8 @@ +// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Appointment Letter Template', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.json b/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.json new file mode 100644 index 00000000000..c136fb22fab --- /dev/null +++ b/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.json @@ -0,0 +1,69 @@ +{ + "actions": [], + "autoname": "HR-APP-LETTER-TEMP-.#####", + "creation": "2019-12-26 12:20:14.219578", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "introduction", + "terms", + "closing_notes" + ], + "fields": [ + { + "fieldname": "introduction", + "fieldtype": "Long Text", + "in_list_view": 1, + "label": "Introduction", + "reqd": 1 + }, + { + "fieldname": "closing_notes", + "fieldtype": "Text", + "label": "Closing Notes" + }, + { + "fieldname": "terms", + "fieldtype": "Table", + "label": "Terms", + "options": "Appointment Letter content", + "reqd": 1 + } + ], + "links": [], + "modified": "2020-01-21 17:00:46.779420", + "modified_by": "Administrator", + "module": "HR", + "name": "Appointment Letter Template", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.py b/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.py new file mode 100644 index 00000000000..c23881f8007 --- /dev/null +++ b/erpnext/hr/doctype/appointment_letter_template/appointment_letter_template.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class AppointmentLetterTemplate(Document): + pass diff --git a/erpnext/hr/doctype/appointment_letter_template/test_appointment_letter_template.py b/erpnext/hr/doctype/appointment_letter_template/test_appointment_letter_template.py new file mode 100644 index 00000000000..3d061ac8e93 --- /dev/null +++ b/erpnext/hr/doctype/appointment_letter_template/test_appointment_letter_template.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestAppointmentLetterTemplate(unittest.TestCase): + pass diff --git a/erpnext/hr/doctype/attendance/attendance.json b/erpnext/hr/doctype/attendance/attendance.json index bc89b368d30..ab2dc4a90fa 100644 --- a/erpnext/hr/doctype/attendance/attendance.json +++ b/erpnext/hr/doctype/attendance/attendance.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "autoname": "naming_series:", "creation": "2013-01-10 16:34:13", @@ -63,6 +64,14 @@ "oldfieldname": "employee_name", "oldfieldtype": "Data" }, + { + "depends_on": "working_hours", + "fieldname": "working_hours", + "fieldtype": "Float", + "label": "Working Hours", + "precision": "1", + "read_only": 1 + }, { "default": "Present", "fieldname": "status", @@ -72,7 +81,7 @@ "no_copy": 1, "oldfieldname": "status", "oldfieldtype": "Select", - "options": "\nPresent\nAbsent\nOn Leave\nHalf Day", + "options": "\nPresent\nAbsent\nOn Leave\nHalf Day\nWork From Home", "reqd": 1, "search_index": 1 }, @@ -126,6 +135,12 @@ "options": "Department", "read_only": 1 }, + { + "fieldname": "shift", + "fieldtype": "Link", + "label": "Shift", + "options": "Shift Type" + }, { "fieldname": "attendance_request", "fieldtype": "Link", @@ -143,20 +158,6 @@ "print_hide": 1, "read_only": 1 }, - { - "depends_on": "working_hours", - "fieldname": "working_hours", - "fieldtype": "Float", - "label": "Working Hours", - "precision": "1", - "read_only": 1 - }, - { - "fieldname": "shift", - "fieldtype": "Link", - "label": "Shift", - "options": "Shift Type" - }, { "default": "0", "fieldname": "late_entry", @@ -173,7 +174,8 @@ "icon": "fa fa-ok", "idx": 1, "is_submittable": 1, - "modified": "2019-07-29 20:35:40.845422", + "links": [], + "modified": "2020-01-27 20:25:29.572281", "modified_by": "Administrator", "module": "HR", "name": "Attendance", diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py index b8081128ff9..9e965dbc392 100644 --- a/erpnext/hr/doctype/attendance/attendance.py +++ b/erpnext/hr/doctype/attendance/attendance.py @@ -7,13 +7,14 @@ import frappe from frappe.utils import getdate, nowdate from frappe import _ from frappe.model.document import Document -from frappe.utils import cstr +from frappe.utils import cstr, get_datetime, get_datetime_str +from frappe.utils import update_progress_bar class Attendance(Document): def validate_duplicate_record(self): res = frappe.db.sql("""select name from `tabAttendance` where employee = %s and attendance_date = %s and name != %s and docstatus != 2""", - (self.employee, self.attendance_date, self.name)) + (self.employee, getdate(self.attendance_date), self.name)) if res: frappe.throw(_("Attendance for employee {0} is already marked").format(self.employee)) @@ -51,7 +52,7 @@ class Attendance(Document): def validate(self): from erpnext.controllers.status_updater import validate_status - validate_status(self.status, ["Present", "Absent", "On Leave", "Half Day"]) + validate_status(self.status, ["Present", "Absent", "On Leave", "Half Day", "Work From Home"]) self.validate_attendance_date() self.validate_duplicate_record() self.check_leave_record() @@ -89,17 +90,85 @@ def add_attendance(events, start, end, conditions=None): if e not in events: events.append(e) -def mark_absent(employee, attendance_date, shift=None): +def mark_attendance(employee, attendance_date, status, shift=None): employee_doc = frappe.get_doc('Employee', employee) if not frappe.db.exists('Attendance', {'employee':employee, 'attendance_date':attendance_date, 'docstatus':('!=', '2')}): doc_dict = { 'doctype': 'Attendance', 'employee': employee, 'attendance_date': attendance_date, - 'status': 'Absent', + 'status': status, 'company': employee_doc.company, 'shift': shift } attendance = frappe.get_doc(doc_dict).insert() attendance.submit() return attendance.name + +@frappe.whitelist() +def mark_bulk_attendance(data): + import json + from pprint import pprint + if isinstance(data, frappe.string_types): + data = json.loads(data) + data = frappe._dict(data) + company = frappe.get_value('Employee', data.employee, 'company') + for date in data.unmarked_days: + doc_dict = { + 'doctype': 'Attendance', + 'employee': data.employee, + 'attendance_date': get_datetime(date), + 'status': data.status, + 'company': company, + } + attendance = frappe.get_doc(doc_dict).insert() + attendance.submit() + + +def get_month_map(): + return frappe._dict({ + "January": 1, + "February": 2, + "March": 3, + "April": 4, + "May": 5, + "June": 6, + "July": 7, + "August": 8, + "September": 9, + "October": 10, + "November": 11, + "December": 12 + }) + +@frappe.whitelist() +def get_unmarked_days(employee, month): + import calendar + month_map = get_month_map() + + today = get_datetime() + + dates_of_month = ['{}-{}-{}'.format(today.year, month_map[month], r) for r in range(1, calendar.monthrange(today.year, month_map[month])[1] + 1)] + + length = len(dates_of_month) + month_start, month_end = dates_of_month[0], dates_of_month[length-1] + + + records = frappe.get_all("Attendance", fields = ['attendance_date', 'employee'] , filters = [ + ["attendance_date", ">", month_start], + ["attendance_date", "<", month_end], + ["employee", "=", employee], + ["docstatus", "!=", 2] + ]) + + marked_days = [get_datetime(record.attendance_date) for record in records] + unmarked_days = [] + + for date in dates_of_month: + date_time = get_datetime(date) + if today.day == date_time.day and today.month == date_time.month: + break + if date_time not in marked_days: + unmarked_days.append(date) + + return unmarked_days diff --git a/erpnext/hr/doctype/attendance/attendance_list.js b/erpnext/hr/doctype/attendance/attendance_list.js index f36fb15a01a..6df3dbd7845 100644 --- a/erpnext/hr/doctype/attendance/attendance_list.js +++ b/erpnext/hr/doctype/attendance/attendance_list.js @@ -1,6 +1,111 @@ frappe.listview_settings['Attendance'] = { add_fields: ["status", "attendance_date"], - get_indicator: function(doc) { - return [__(doc.status), doc.status=="Present" ? "green" : "darkgrey", "status,=," + doc.status]; + get_indicator: function (doc) { + if (["Present", "Work From Home"].includes(doc.status)) { + return [__(doc.status), "green", "status,=," + doc.status]; + } else if (["Absent", "On Leave"].includes(doc.status)) { + return [__(doc.status), "red", "status,=," + doc.status]; + } else if (doc.status == "Half Day") { + return [__(doc.status), "orange", "status,=," + doc.status]; + } + }, + onload: function(list_view) { + let me = this; + const months = moment.months() + list_view.page.add_inner_button( __("Mark Attendance"), function(){ + let dialog = new frappe.ui.Dialog({ + title: __("Mark Attendance"), + fields: [ + { + fieldname: 'employee', + label: __('For Employee'), + fieldtype: 'Link', + options: 'Employee', + reqd: 1, + onchange: function(){ + dialog.set_df_property("unmarked_days", "hidden", 1); + dialog.set_df_property("status", "hidden", 1); + dialog.set_df_property("month", "value", ''); + dialog.set_df_property("unmarked_days", "options", []); + } + }, + { + label: __("For Month"), + fieldtype: "Select", + fieldname: "month", + options: months, + reqd: 1, + onchange: function(){ + if(dialog.fields_dict.employee.value && dialog.fields_dict.month.value) { + dialog.set_df_property("status", "hidden", 0); + dialog.set_df_property("unmarked_days", "options", []); + me.get_multi_select_options(dialog.fields_dict.employee.value, dialog.fields_dict.month.value).then(options =>{ + dialog.set_df_property("unmarked_days", "hidden", 0); + dialog.set_df_property("unmarked_days", "options", options); + }); + } + } + }, + { + label: __("Status"), + fieldtype: "Select", + fieldname: "status", + options: ["Present", "Absent", "Half Day", "Work From Home"], + hidden:1, + reqd: 1, + + }, + { + label: __("Unmarked Attendance for days"), + fieldname: "unmarked_days", + fieldtype: "MultiCheck", + options: [], + columns: 2, + hidden: 1 + }, + ], + primary_action(data){ + frappe.confirm(__('Mark attendance as ' + data.status + ' for ' + data.month +'' + ' on selected dates?'), () => { + frappe.call({ + method: "erpnext.hr.doctype.attendance.attendance.mark_bulk_attendance", + args: { + data : data + }, + callback: function(r) { + if(r.message === 1) { + frappe.show_alert({message:__("Attendance Marked"), indicator:'blue'}); + cur_dialog.hide(); + } + } + }); + }); + dialog.hide(); + list_view.refresh(); + }, + primary_action_label: __('Mark Attendance') + + }); + dialog.show(); + }); + }, + get_multi_select_options: function(employee, month){ + return new Promise(resolve => { + frappe.call({ + method: 'erpnext.hr.doctype.attendance.attendance.get_unmarked_days', + async: false, + args:{ + employee: employee, + month: month, + } + }).then(r => { + var options = []; + for(var d in r.message){ + var momentObj = moment(r.message[d], 'YYYY-MM-DD'); + var date = momentObj.format('DD-MM-YYYY'); + options.push({ "label":date, "value": r.message[d] , "checked": 1}); + } + resolve(options); + }); + }); } }; diff --git a/erpnext/hr/doctype/attendance/test_attendance.py b/erpnext/hr/doctype/attendance/test_attendance.py index 35d1126dc10..838b704c5a5 100644 --- a/erpnext/hr/doctype/attendance/test_attendance.py +++ b/erpnext/hr/doctype/attendance/test_attendance.py @@ -14,7 +14,7 @@ class TestAttendance(unittest.TestCase): employee = make_employee("test_mark_absent@example.com") date = nowdate() frappe.db.delete('Attendance', {'employee':employee, 'attendance_date':date}) - from erpnext.hr.doctype.attendance.attendance import mark_absent - attendance = mark_absent(employee, date) + from erpnext.hr.doctype.attendance.attendance import mark_attendance + attendance = mark_attendance(employee, date, 'Absent') fetch_attendance = frappe.get_value('Attendance', {'employee':employee, 'attendance_date':date, 'status':'Absent'}) self.assertEqual(attendance, fetch_attendance) diff --git a/erpnext/hr/doctype/attendance_request/attendance_request.json b/erpnext/hr/doctype/attendance_request/attendance_request.json index bcd3331da1e..837a87824fa 100644 --- a/erpnext/hr/doctype/attendance_request/attendance_request.json +++ b/erpnext/hr/doctype/attendance_request/attendance_request.json @@ -1,575 +1,190 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "HR-ARQ-.YY.-.MM.-.#####", - "beta": 0, - "creation": "2018-04-13 15:37:40.918990", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "HR-ARQ-.YY.-.MM.-.#####", + "creation": "2018-04-13 15:37:40.918990", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "department", + "column_break_5", + "company", + "from_date", + "to_date", + "half_day", + "half_day_date", + "reason_section", + "reason", + "column_break_4", + "explanation", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Employee Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "remember_last_selected_value": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_5", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "from_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "From Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "from_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "From Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "to_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "To Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "to_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "To Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "half_day", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Half Day", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "half_day", + "fieldtype": "Check", + "label": "Half Day" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "half_day", - "fieldname": "half_day_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Half Day Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "half_day", + "fieldname": "half_day_date", + "fieldtype": "Date", + "label": "Half Day Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reason_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reason", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "reason_section", + "fieldtype": "Section Break", + "label": "Reason" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reason", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Reason", - "length": 0, - "no_copy": 0, - "options": "Work From Home\nOn Duty", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "reason", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Reason", + "options": "Work From Home\nOn Duty", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "explanation", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Explanation", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "explanation", + "fieldtype": "Small Text", + "label": "Explanation" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Attendance Request", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Attendance Request", + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 17:09:48.117407", - "modified_by": "Administrator", - "module": "HR", - "name": "Attendance Request", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2019-12-16 11:49:26.943173", + "modified_by": "Administrator", + "module": "HR", + "name": "Attendance Request", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "employee_name", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "employee_name", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/hr/doctype/attendance_request/attendance_request.py b/erpnext/hr/doctype/attendance_request/attendance_request.py index a4598a798e2..090d53262cf 100644 --- a/erpnext/hr/doctype/attendance_request/attendance_request.py +++ b/erpnext/hr/doctype/attendance_request/attendance_request.py @@ -38,6 +38,8 @@ class AttendanceRequest(Document): attendance.employee_name = self.employee_name if self.half_day and date_diff(getdate(self.half_day_date), getdate(attendance_date)) == 0: attendance.status = "Half Day" + elif self.reason == "Work From Home": + attendance.status = "Work From Home" else: attendance.status = "Present" attendance.attendance_date = attendance_date diff --git a/erpnext/hr/doctype/attendance_request/test_attendance_request.py b/erpnext/hr/doctype/attendance_request/test_attendance_request.py index a2c39d9a710..92b1eaee2c7 100644 --- a/erpnext/hr/doctype/attendance_request/test_attendance_request.py +++ b/erpnext/hr/doctype/attendance_request/test_attendance_request.py @@ -13,7 +13,28 @@ class TestAttendanceRequest(unittest.TestCase): for doctype in ["Attendance Request", "Attendance"]: frappe.db.sql("delete from `tab{doctype}`".format(doctype=doctype)) - def test_attendance_request(self): + def test_on_duty_attendance_request(self): + today = nowdate() + employee = get_employee() + attendance_request = frappe.new_doc("Attendance Request") + attendance_request.employee = employee.name + attendance_request.from_date = date(date.today().year, 1, 1) + attendance_request.to_date = date(date.today().year, 1, 2) + attendance_request.reason = "On Duty" + attendance_request.company = "_Test Company" + attendance_request.insert() + attendance_request.submit() + attendance = frappe.get_doc('Attendance', { + 'employee': employee.name, + 'attendance_date': date(date.today().year, 1, 1), + 'docstatus': 1 + }) + self.assertEqual(attendance.status, 'Present') + attendance_request.cancel() + attendance.reload() + self.assertEqual(attendance.docstatus, 2) + + def test_work_from_home_attendance_request(self): today = nowdate() employee = get_employee() attendance_request = frappe.new_doc("Attendance Request") @@ -29,7 +50,7 @@ class TestAttendanceRequest(unittest.TestCase): 'attendance_date': date(date.today().year, 1, 1), 'docstatus': 1 }) - self.assertEqual(attendance.status, 'Present') + self.assertEqual(attendance.status, 'Work From Home') attendance_request.cancel() attendance.reload() self.assertEqual(attendance.docstatus, 2) diff --git a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py index bc4a1b4034b..7a9727f18c4 100644 --- a/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py +++ b/erpnext/hr/doctype/compensatory_leave_request/compensatory_leave_request.py @@ -5,9 +5,10 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import date_diff, add_days, getdate +from frappe.utils import date_diff, add_days, getdate, cint from frappe.model.document import Document -from erpnext.hr.utils import validate_dates, validate_overlap, get_leave_period, get_holidays_for_employee +from erpnext.hr.utils import validate_dates, validate_overlap, get_leave_period, \ + get_holidays_for_employee, create_additional_leave_ledger_entry class CompensatoryLeaveRequest(Document): @@ -25,16 +26,14 @@ class CompensatoryLeaveRequest(Document): frappe.throw(_("Leave Type is madatory")) def validate_attendance(self): - query = """select attendance_date, status - from `tabAttendance` where - attendance_date between %(work_from_date)s and %(work_end_date)s - and docstatus=1 and status = 'Present' and employee=%(employee)s""" + attendance = frappe.get_all('Attendance', + filters={ + 'attendance_date': ['between', (self.work_from_date, self.work_end_date)], + 'status': 'Present', + 'docstatus': 1, + 'employee': self.employee + }, fields=['attendance_date', 'status']) - attendance = frappe.db.sql(query, { - "work_from_date": self.work_from_date, - "work_end_date": self.work_end_date, - "employee": self.employee - }, as_dict=True) if len(attendance) < date_diff(self.work_end_date, self.work_from_date) + 1: frappe.throw(_("You are not present all day(s) between compensatory leave request days")) @@ -50,13 +49,19 @@ class CompensatoryLeaveRequest(Document): date_difference -= 0.5 leave_period = get_leave_period(self.work_from_date, self.work_end_date, company) if leave_period: - leave_allocation = self.exists_allocation_for_period(leave_period) + leave_allocation = self.get_existing_allocation_for_period(leave_period) if leave_allocation: leave_allocation.new_leaves_allocated += date_difference - leave_allocation.submit() + leave_allocation.validate() + leave_allocation.db_set("new_leaves_allocated", leave_allocation.total_leaves_allocated) + leave_allocation.db_set("total_leaves_allocated", leave_allocation.total_leaves_allocated) + + # generate additional ledger entry for the new compensatory leaves off + create_additional_leave_ledger_entry(leave_allocation, date_difference, add_days(self.work_end_date, 1)) + else: leave_allocation = self.create_leave_allocation(leave_period, date_difference) - self.db_set("leave_allocation", leave_allocation.name) + self.leave_allocation=leave_allocation.name else: frappe.throw(_("There is no leave period in between {0} and {1}").format(self.work_from_date, self.work_end_date)) @@ -68,11 +73,16 @@ class CompensatoryLeaveRequest(Document): leave_allocation = frappe.get_doc("Leave Allocation", self.leave_allocation) if leave_allocation: leave_allocation.new_leaves_allocated -= date_difference - if leave_allocation.total_leaves_allocated - date_difference <= 0: - leave_allocation.total_leaves_allocated = 0 - leave_allocation.submit() + if leave_allocation.new_leaves_allocated - date_difference <= 0: + leave_allocation.new_leaves_allocated = 0 + leave_allocation.validate() + leave_allocation.db_set("new_leaves_allocated", leave_allocation.total_leaves_allocated) + leave_allocation.db_set("total_leaves_allocated", leave_allocation.total_leaves_allocated) - def exists_allocation_for_period(self, leave_period): + # create reverse entry on cancelation + create_additional_leave_ledger_entry(leave_allocation, date_difference * -1, add_days(self.work_end_date, 1)) + + def get_existing_allocation_for_period(self, leave_period): leave_allocation = frappe.db.sql(""" select name from `tabLeave Allocation` @@ -95,17 +105,18 @@ class CompensatoryLeaveRequest(Document): def create_leave_allocation(self, leave_period, date_difference): is_carry_forward = frappe.db.get_value("Leave Type", self.leave_type, "is_carry_forward") - allocation = frappe.new_doc("Leave Allocation") - allocation.employee = self.employee - allocation.employee_name = self.employee_name - allocation.leave_type = self.leave_type - allocation.from_date = add_days(self.work_end_date, 1) - allocation.to_date = leave_period[0].to_date - allocation.new_leaves_allocated = date_difference - allocation.total_leaves_allocated = date_difference - allocation.description = self.reason - if is_carry_forward == 1: - allocation.carry_forward = True - allocation.save(ignore_permissions = True) + allocation = frappe.get_doc(dict( + doctype="Leave Allocation", + employee=self.employee, + employee_name=self.employee_name, + leave_type=self.leave_type, + from_date=add_days(self.work_end_date, 1), + to_date=leave_period[0].to_date, + carry_forward=cint(is_carry_forward), + new_leaves_allocated=date_difference, + total_leaves_allocated=date_difference, + description=self.reason + )) + allocation.insert(ignore_permissions=True) allocation.submit() - return allocation + return allocation \ No newline at end of file diff --git a/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py b/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py index f2ca1f4f5f0..1615ab30f1d 100644 --- a/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py +++ b/erpnext/hr/doctype/compensatory_leave_request/test_compensatory_leave_request.py @@ -5,37 +5,128 @@ from __future__ import unicode_literals import frappe import unittest +from frappe.utils import today, add_months, add_days +from erpnext.hr.doctype.attendance_request.test_attendance_request import get_employee +from erpnext.hr.doctype.leave_period.test_leave_period import create_leave_period +from erpnext.hr.doctype.leave_application.leave_application import get_leave_balance_on -# class TestCompensatoryLeaveRequest(unittest.TestCase): -# def get_compensatory_leave_request(self): -# return frappe.get_doc('Compensatory Leave Request', dict( -# employee = employee, -# work_from_date = today, -# work_to_date = today, -# reason = 'test' -# )).insert() -# -# def test_creation_of_leave_allocation(self): -# employee = get_employee() -# today = get_today() -# -# compensatory_leave_request = self.get_compensatory_leave_request(today) -# -# before = get_leave_balance(employee, compensatory_leave_request.leave_type) -# -# compensatory_leave_request.submit() -# -# self.assertEqual(get_leave_balance(employee, compensatory_leave_request.leave_type), before + 1) -# -# def test_max_compensatory_leave(self): -# employee = get_employee() -# today = get_today() -# -# compensatory_leave_request = self.get_compensatory_leave_request() -# -# frappe.db.set_value('Leave Type', compensatory_leave_request.leave_type, 'max_leaves_allowed', 0) -# -# self.assertRaises(MaxLeavesLimitCrossed, compensatory_leave_request.submit) -# -# frappe.db.set_value('Leave Type', compensatory_leave_request.leave_type, 'max_leaves_allowed', 10) -# +class TestCompensatoryLeaveRequest(unittest.TestCase): + def setUp(self): + frappe.db.sql(''' delete from `tabCompensatory Leave Request`''') + frappe.db.sql(''' delete from `tabLeave Ledger Entry`''') + frappe.db.sql(''' delete from `tabLeave Allocation`''') + frappe.db.sql(''' delete from `tabAttendance` where attendance_date in {0} '''.format((today(), add_days(today(), -1)))) #nosec + create_leave_period(add_months(today(), -3), add_months(today(), 3), "_Test Company") + create_holiday_list() + + employee = get_employee() + employee.holiday_list = "_Test Compensatory Leave" + employee.save() + + def test_leave_balance_on_submit(self): + ''' check creation of leave allocation on submission of compensatory leave request ''' + employee = get_employee() + mark_attendance(employee) + compensatory_leave_request = get_compensatory_leave_request(employee.name) + + before = get_leave_balance_on(employee.name, compensatory_leave_request.leave_type, today()) + compensatory_leave_request.submit() + + self.assertEqual(get_leave_balance_on(employee.name, compensatory_leave_request.leave_type, add_days(today(), 1)), before + 1) + + def test_leave_allocation_update_on_submit(self): + employee = get_employee() + mark_attendance(employee, date=add_days(today(), -1)) + compensatory_leave_request = get_compensatory_leave_request(employee.name, leave_date=add_days(today(), -1)) + compensatory_leave_request.submit() + + # leave allocation creation on submit + leaves_allocated = frappe.db.get_value('Leave Allocation', { + 'name': compensatory_leave_request.leave_allocation + }, ['total_leaves_allocated']) + self.assertEqual(leaves_allocated, 1) + + mark_attendance(employee) + compensatory_leave_request = get_compensatory_leave_request(employee.name) + compensatory_leave_request.submit() + + # leave allocation updates on submission of second compensatory leave request + leaves_allocated = frappe.db.get_value('Leave Allocation', { + 'name': compensatory_leave_request.leave_allocation + }, ['total_leaves_allocated']) + self.assertEqual(leaves_allocated, 2) + + def test_creation_of_leave_ledger_entry_on_submit(self): + ''' check creation of leave ledger entry on submission of leave request ''' + employee = get_employee() + mark_attendance(employee) + compensatory_leave_request = get_compensatory_leave_request(employee.name) + compensatory_leave_request.submit() + + filters = dict(transaction_name=compensatory_leave_request.leave_allocation) + leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=filters) + + self.assertEquals(len(leave_ledger_entry), 1) + self.assertEquals(leave_ledger_entry[0].employee, compensatory_leave_request.employee) + self.assertEquals(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type) + self.assertEquals(leave_ledger_entry[0].leaves, 1) + + # check reverse leave ledger entry on cancellation + compensatory_leave_request.cancel() + leave_ledger_entry = frappe.get_all('Leave Ledger Entry', fields='*', filters=filters, order_by = 'creation desc') + + self.assertEquals(len(leave_ledger_entry), 2) + self.assertEquals(leave_ledger_entry[0].employee, compensatory_leave_request.employee) + self.assertEquals(leave_ledger_entry[0].leave_type, compensatory_leave_request.leave_type) + self.assertEquals(leave_ledger_entry[0].leaves, -1) + +def get_compensatory_leave_request(employee, leave_date=today()): + prev_comp_leave_req = frappe.db.get_value('Compensatory Leave Request', + dict(leave_type='Compensatory Off', + work_from_date=leave_date, + work_end_date=leave_date, + employee=employee), 'name') + if prev_comp_leave_req: + return frappe.get_doc('Compensatory Leave Request', prev_comp_leave_req) + + return frappe.get_doc(dict( + doctype='Compensatory Leave Request', + employee=employee, + leave_type='Compensatory Off', + work_from_date=leave_date, + work_end_date=leave_date, + reason='test' + )).insert() + +def mark_attendance(employee, date=today(), status='Present'): + if not frappe.db.exists(dict(doctype='Attendance', employee=employee.name, attendance_date=date, status='Present')): + attendance = frappe.get_doc({ + "doctype": "Attendance", + "employee": employee.name, + "attendance_date": date, + "status": status + }) + attendance.save() + attendance.submit() + +def create_holiday_list(): + if frappe.db.exists("Holiday List", "_Test Compensatory Leave"): + return + + holiday_list = frappe.get_doc({ + "doctype": "Holiday List", + "from_date": add_months(today(), -3), + "to_date": add_months(today(), 3), + "holidays": [ + { + "description": "Test Holiday", + "holiday_date": today() + }, + { + "description": "Test Holiday 1", + "holiday_date": add_days(today(), -1) + } + ], + "holiday_list_name": "_Test Compensatory Leave" + }) + holiday_list.save() \ No newline at end of file diff --git a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py index 25cda4444bc..1cc23812f75 100644 --- a/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py +++ b/erpnext/hr/doctype/daily_work_summary/daily_work_summary.py @@ -97,9 +97,9 @@ class DailyWorkSummary(Document): return dict(replies=replies, original_message=dws_group.message, - title=_('Work Summary for {0}'.format( + title=_('Work Summary for {0}').format( global_date_format(self.creation) - )), + ), did_not_reply=', '.join(did_not_reply) or '', did_not_reply_title=_('No replies from')) diff --git a/erpnext/hr/doctype/department/department.json b/erpnext/hr/doctype/department/department.json index 3b400ce8d76..a191b6b8228 100644 --- a/erpnext/hr/doctype/department/department.json +++ b/erpnext/hr/doctype/department/department.json @@ -1,569 +1,180 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, + "actions": [], "allow_import": 1, "allow_rename": 1, - "autoname": "", - "beta": 0, "creation": "2013-02-05 11:48:26", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, + "engine": "InnoDB", + "field_order": [ + "department_name", + "parent_department", + "column_break_3", + "company", + "is_group", + "disabled", + "section_break_4", + "leave_block_list", + "leave_section", + "leave_approvers", + "expense_section", + "expense_approvers", + "lft", + "rgt", + "old_parent" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "department_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Department", - "length": 0, - "no_copy": 0, "oldfieldname": "department_name", "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "parent_department", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Parent Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Department" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "company", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, "label": "Company", - "length": 0, - "no_copy": 0, "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, "bold": 1, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "is_group", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, - "label": "Is Group", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Is Group" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, + "default": "0", "fieldname": "disabled", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Disabled", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Disabled" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "section_break_4", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "Days for which Holidays are blocked for this department.", - "fetch_if_empty": 0, "fieldname": "leave_block_list", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Leave Block List", - "length": 0, - "no_copy": 0, - "options": "Leave Block List", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Leave Block List" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "leave_section", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Leave Approvers", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Leave Approvers" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "The first Leave Approver in the list will be set as the default Leave Approver.", - "fetch_if_empty": 0, "fieldname": "leave_approvers", "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Leave Approver", - "length": 0, - "no_copy": 0, - "options": "Department Approver", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Department Approver" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "expense_section", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Expense Approvers", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Expense Approvers" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "description": "The first Expense Approver in the list will be set as the default Expense Approver.", - "fetch_if_empty": 0, "fieldname": "expense_approvers", "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Expense Approver", - "length": 0, - "no_copy": 0, - "options": "Department Approver", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Department Approver" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "lft", "fieldtype": "Int", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "lft", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "rgt", "fieldtype": "Int", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "rgt", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "old_parent", "fieldtype": "Data", "hidden": 1, "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Old Parent", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "print_hide": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "fa fa-sitemap", "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-06-25 18:43:05.550387", + "links": [], + "modified": "2019-12-12 14:48:35.254308", "modified_by": "Administrator", "module": "HR", "name": "Department", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "HR User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "Academics User", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 }, { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, "import": 1, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "HR Manager", "set_user_permissions": 1, "share": 1, - "submit": 0, "write": 1 } ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, "show_name_in_global_search": 1, - "sort_order": "ASC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "sort_field": "modified", + "sort_order": "ASC" } \ No newline at end of file diff --git a/erpnext/hr/doctype/department_approver/department_approver.py b/erpnext/hr/doctype/department_approver/department_approver.py index 9f2f2013a77..df0f75a18c3 100644 --- a/erpnext/hr/doctype/department_approver/department_approver.py +++ b/erpnext/hr/doctype/department_approver/department_approver.py @@ -19,14 +19,19 @@ def get_approvers(doctype, txt, searchfield, start, page_len, filters): approvers = [] department_details = {} department_list = [] - employee_department = filters.get("department") or frappe.get_value("Employee", filters.get("employee"), "department") + employee = frappe.get_value("Employee", filters.get("employee"), ["department", "leave_approver"], as_dict=True) + + employee_department = filters.get("department") or employee.department if employee_department: department_details = frappe.db.get_value("Department", {"name": employee_department}, ["lft", "rgt"], as_dict=True) if department_details: department_list = frappe.db.sql("""select name from `tabDepartment` where lft <= %s and rgt >= %s and disabled=0 - order by lft desc""", (department_details.lft, department_details.rgt), as_list = True) + order by lft desc""", (department_details.lft, department_details.rgt), as_list=True) + + if filters.get("doctype") == "Leave Application" and employee.leave_approver: + approvers.append(frappe.db.get_value("User", employee.leave_approver, ['name', 'first_name', 'last_name'])) if filters.get("doctype") == "Leave Application": parentfield = "leave_approvers" @@ -41,4 +46,4 @@ def get_approvers(doctype, txt, searchfield, start, page_len, filters): and approver.parentfield = %s and approver.approver=user.name""",(d, "%" + txt + "%", parentfield), as_list=True) - return approvers \ No newline at end of file + return set(tuple(approver) for approver in approvers) diff --git a/erpnext/hr/doctype/driving_license_category/driving_license_category.json b/erpnext/hr/doctype/driving_license_category/driving_license_category.json index 45e38d39c92..8bc61a808a8 100644 --- a/erpnext/hr/doctype/driving_license_category/driving_license_category.json +++ b/erpnext/hr/doctype/driving_license_category/driving_license_category.json @@ -1,161 +1,51 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-10-17 08:19:43.142329", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2017-10-17 08:19:43.142329", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "class", + "description", + "issuing_date", + "expiry_date" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "class", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Class", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "class", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Driver licence class" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "description", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Description" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "issuing_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Issuing Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "issuing_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Issuing Date" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "expiry_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Expiry Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "expiry_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Expiry Date" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2017-10-17 08:19:43.142329", - "modified_by": "Administrator", - "module": "HR", - "name": "Driving License Category", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "istable": 1, + "links": [], + "modified": "2019-12-12 14:40:55.209022", + "modified_by": "Administrator", + "module": "HR", + "name": "Driving License Category", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json index 9291820524e..3f0b9c49ac3 100644 --- a/erpnext/hr/doctype/employee/employee.json +++ b/erpnext/hr/doctype/employee/employee.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_events_in_timeline": 1, "allow_import": 1, "allow_rename": 1, @@ -12,24 +13,25 @@ "basic_information", "employee", "naming_series", - "salutation", "first_name", "middle_name", "last_name", + "salutation", "employee_name", + "employment_type", "image", "column_break1", "company", "status", - "employee_number", - "employment_type", "gender", "date_of_birth", "date_of_joining", + "employee_number", "emergency_contact_details", - "emergency_phone_number", "person_to_be_contacted", "relation", + "column_break_19", + "emergency_phone_number", "erpnext_user", "user_id", "create_user", @@ -52,10 +54,10 @@ "attendance_and_leave_details", "leave_policy", "attendance_device_id", + "leave_approver", "column_break_44", "holiday_list", "default_shift", - "leave_approver", "salary_information", "salary_mode", "bank_name", @@ -65,14 +67,14 @@ "health_insurance_no", "contact_details", "cell_number", - "prefered_contact_email", "prefered_email", - "company_email", "personal_email", "unsubscribed", - "column_break4", "permanent_accommodation_type", "permanent_address", + "column_break4", + "prefered_contact_email", + "company_email", "current_accommodation_type", "current_address", "sb53", @@ -82,9 +84,9 @@ "date_of_issue", "valid_upto", "place_of_issue", - "column_break6", "marital_status", "blood_group", + "column_break6", "family_background", "health_details", "educational_qualification", @@ -232,7 +234,6 @@ "reqd": 1 }, { - "description": "You can enter any date manually", "fieldname": "date_of_birth", "fieldtype": "Date", "label": "Date of Birth", @@ -250,7 +251,6 @@ }, { "allow_in_quick_entry": 1, - "collapsible": 1, "fieldname": "emergency_contact_details", "fieldtype": "Section Break", "label": "Emergency Contact" @@ -265,7 +265,7 @@ "bold": 1, "fieldname": "person_to_be_contacted", "fieldtype": "Data", - "label": "Emergency Contact" + "label": "Emergency Contact Name" }, { "fieldname": "relation", @@ -777,12 +777,17 @@ "fieldtype": "Link", "label": "Leave Approver", "options": "User" + }, + { + "fieldname": "column_break_19", + "fieldtype": "Column Break" } ], "icon": "fa fa-user", "idx": 24, "image_field": "image", - "modified": "2019-09-12 14:21:12.711280", + "links": [], + "modified": "2020-01-09 04:23:55.611366", "modified_by": "Administrator", "module": "HR", "name": "Employee", @@ -831,4 +836,4 @@ "sort_order": "DESC", "title_field": "employee_name", "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/hr/doctype/employee/employee.py b/erpnext/hr/doctype/employee/employee.py index 3fc330e2d26..4d49503d2dc 100755 --- a/erpnext/hr/doctype/employee/employee.py +++ b/erpnext/hr/doctype/employee/employee.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe -from frappe.utils import getdate, validate_email_address, today, add_years, format_datetime +from frappe.utils import getdate, validate_email_address, today, add_years, format_datetime, cstr from frappe.model.naming import set_name_by_naming_series from frappe import throw, _, scrub from frappe.permissions import add_user_permission, remove_user_permission, \ @@ -152,8 +152,8 @@ class Employee(NestedSet): elif self.date_of_retirement and self.date_of_joining and (getdate(self.date_of_retirement) <= getdate(self.date_of_joining)): throw(_("Date Of Retirement must be greater than Date of Joining")) - elif self.relieving_date and self.date_of_joining and (getdate(self.relieving_date) <= getdate(self.date_of_joining)): - throw(_("Relieving Date must be greater than Date of Joining")) + elif self.relieving_date and self.date_of_joining and (getdate(self.relieving_date) < getdate(self.date_of_joining)): + throw(_("Relieving Date must be greater than or equal to Date of Joining")) elif self.contract_end_date and self.date_of_joining and (getdate(self.contract_end_date) <= getdate(self.date_of_joining)): throw(_("Contract End Date must be greater than Date of Joining")) @@ -164,13 +164,20 @@ class Employee(NestedSet): if self.personal_email: validate_email_address(self.personal_email, True) + def set_preferred_email(self): + preferred_email_field = frappe.scrub(self.prefered_contact_email) + if preferred_email_field: + preferred_email = self.get(preferred_email_field) + self.prefered_email = preferred_email + def validate_status(self): if self.status == 'Left': reports_to = frappe.db.get_all('Employee', - filters={'reports_to': self.name} + filters={'reports_to': self.name, 'status': "Active"}, + fields=['name','employee_name'] ) if reports_to: - link_to_employees = [frappe.utils.get_link_to_form('Employee', employee.name) for employee in reports_to] + link_to_employees = [frappe.utils.get_link_to_form('Employee', employee.name, label=employee.employee_name) for employee in reports_to] throw(_("Employee status cannot be set to 'Left' as following employees are currently reporting to this employee: ") + ', '.join(link_to_employees), EmployeeLeftValidationError) if not self.relieving_date: @@ -217,8 +224,8 @@ class Employee(NestedSet): def reset_employee_emails_cache(self): prev_doc = self.get_doc_before_save() or {} - cell_number = self.get('cell_number') - prev_number = prev_doc.get('cell_number') + cell_number = cstr(self.get('cell_number')) + prev_number = cstr(prev_doc.get('cell_number')) if (cell_number != prev_number or self.get('user_id') != prev_doc.get('user_id')): frappe.cache().hdel('employees_with_number', cell_number) diff --git a/erpnext/hr/doctype/employee/employee_dashboard.py b/erpnext/hr/doctype/employee/employee_dashboard.py index 162b697ac86..11ad83ba37e 100644 --- a/erpnext/hr/doctype/employee/employee_dashboard.py +++ b/erpnext/hr/doctype/employee/employee_dashboard.py @@ -21,7 +21,7 @@ def get_data(): }, { 'label': _('Expense'), - 'items': ['Expense Claim', 'Travel Request'] + 'items': ['Expense Claim', 'Travel Request', 'Employee Advance'] }, { 'label': _('Benefit'), diff --git a/erpnext/hr/doctype/employee/test_employee.py b/erpnext/hr/doctype/employee/test_employee.py index 5a63beb2836..d3410de2eb7 100644 --- a/erpnext/hr/doctype/employee/test_employee.py +++ b/erpnext/hr/doctype/employee/test_employee.py @@ -45,7 +45,7 @@ class TestEmployee(unittest.TestCase): employee1_doc.status = 'Left' self.assertRaises(EmployeeLeftValidationError, employee1_doc.save) -def make_employee(user): +def make_employee(user, company=None): if not frappe.db.get_value("User", user): frappe.get_doc({ "doctype": "User", @@ -55,12 +55,12 @@ def make_employee(user): "roles": [{"doctype": "Has Role", "role": "Employee"}] }).insert() - if not frappe.db.get_value("Employee", {"user_id": user}): + if not frappe.db.get_value("Employee", { "user_id": user, "company": company or erpnext.get_default_company() }): employee = frappe.get_doc({ "doctype": "Employee", "naming_series": "EMP-", "first_name": user, - "company": erpnext.get_default_company(), + "company": company or erpnext.get_default_company(), "user_id": user, "date_of_birth": "1990-05-08", "date_of_joining": "2013-01-01", diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.js b/erpnext/hr/doctype/employee_advance/employee_advance.js index 77a2bbcbc07..389660387b7 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.js +++ b/erpnext/hr/doctype/employee_advance/employee_advance.js @@ -34,7 +34,7 @@ frappe.ui.form.on('Employee Advance', { } else if ( frm.doc.docstatus === 1 - && flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount) + && flt(frm.doc.claimed_amount) < flt(frm.doc.paid_amount) - flt(frm.doc.return_amount) && frappe.model.can_create("Expense Claim") ) { frm.add_custom_button( @@ -45,6 +45,15 @@ frappe.ui.form.on('Employee Advance', { __('Create') ); } + + if (frm.doc.docstatus === 1 + && (flt(frm.doc.claimed_amount) + flt(frm.doc.return_amount) < flt(frm.doc.paid_amount)) + && frappe.model.can_create("Journal Entry")) { + + frm.add_custom_button(__("Return"), function() { + frm.trigger('make_return_entry'); + }, __('Create')); + } }, make_payment_entry: function(frm) { @@ -83,6 +92,24 @@ frappe.ui.form.on('Employee Advance', { }); }, + make_return_entry: function(frm) { + frappe.call({ + method: 'erpnext.hr.doctype.employee_advance.employee_advance.make_return_entry', + args: { + 'employee': frm.doc.employee, + 'company': frm.doc.company, + 'employee_advance_name': frm.doc.name, + 'return_amount': flt(frm.doc.paid_amount - frm.doc.claimed_amount), + 'advance_account': frm.doc.advance_account, + 'mode_of_payment': frm.doc.mode_of_payment + }, + callback: function(r) { + const doclist = frappe.model.sync(r.message); + frappe.set_route('Form', doclist[0].doctype, doclist[0].name); + } + }); + }, + employee: function (frm) { if (frm.doc.employee) { return frappe.call({ diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.json b/erpnext/hr/doctype/employee_advance/employee_advance.json index 3597e76f1e6..d233a2bb936 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.json +++ b/erpnext/hr/doctype/employee_advance/employee_advance.json @@ -1,737 +1,213 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2017-10-09 14:26:29.612365", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2017-10-09 14:26:29.612365", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "naming_series", + "employee", + "employee_name", + "column_break_4", + "posting_date", + "department", + "section_break_8", + "purpose", + "column_break_11", + "advance_amount", + "paid_amount", + "due_advance_amount", + "claimed_amount", + "return_amount", + "section_break_7", + "status", + "company", + "amended_from", + "column_break_18", + "advance_account", + "mode_of_payment" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Series", - "length": 0, - "no_copy": 0, - "options": "HR-EAD-.YYYY.-", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "HR-EAD-.YYYY.-" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Employee Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Read Only", + "label": "Employee Name" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "posting_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Posting Date", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Posting Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_8", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_8", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "purpose", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Purpose", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "purpose", + "fieldtype": "Small Text", + "in_list_view": 1, + "label": "Purpose", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_11", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "advance_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Advance Amount", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "advance_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Advance Amount", + "options": "Company:company:default_currency", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "paid_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Paid Amount", - "length": 0, - "no_copy": 1, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "paid_amount", + "fieldtype": "Currency", + "label": "Paid Amount", + "no_copy": 1, + "options": "Company:company:default_currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:cur_frm.doc.employee", - "fieldname": "due_advance_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Due Advance Amount", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:cur_frm.doc.employee", + "fieldname": "due_advance_amount", + "fieldtype": "Currency", + "label": "Due Advance Amount", + "options": "Company:company:default_currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "claimed_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Claimed Amount", - "length": 0, - "no_copy": 1, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "claimed_amount", + "fieldtype": "Currency", + "label": "Claimed Amount", + "no_copy": 1, + "options": "Company:company:default_currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_7", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_7", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 1, - "options": "Draft\nPaid\nUnpaid\nClaimed\nCancelled", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "no_copy": 1, + "options": "Draft\nPaid\nUnpaid\nClaimed\nCancelled", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Employee Advance", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Employee Advance", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_18", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "advance_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Advance Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "advance_account", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Advance Account", + "options": "Account", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mode_of_payment", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Mode of Payment", - "length": 0, - "no_copy": 0, - "options": "Mode of Payment", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "mode_of_payment", + "fieldtype": "Link", + "label": "Mode of Payment", + "options": "Mode of Payment" + }, + { + "fieldname": "return_amount", + "fieldtype": "Currency", + "label": "Returned Amount", + "options": "Company:company:default_currency", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-01-30 11:28:15.529649", - "modified_by": "Administrator", - "module": "HR", - "name": "Employee Advance", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2019-12-15 19:04:07.044505", + "modified_by": "Administrator", + "module": "HR", + "name": "Employee Advance", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Expense Approver", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Expense Approver", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "employee,employee_name", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} \ No newline at end of file + ], + "search_fields": "employee,employee_name", + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} diff --git a/erpnext/hr/doctype/employee_advance/employee_advance.py b/erpnext/hr/doctype/employee_advance/employee_advance.py index 7813da78ca4..f10e3b6ce26 100644 --- a/erpnext/hr/doctype/employee_advance/employee_advance.py +++ b/erpnext/hr/doctype/employee_advance/employee_advance.py @@ -7,6 +7,7 @@ import frappe, erpnext from frappe import _ from frappe.model.document import Document from frappe.utils import flt, nowdate +from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account class EmployeeAdvanceOverPayment(frappe.ValidationError): pass @@ -53,11 +54,25 @@ class EmployeeAdvance(Document): and party = %s """, (self.name, self.employee), as_dict=1)[0].paid_amount + return_amount = frappe.db.sql(""" + select name, ifnull(sum(credit_in_account_currency), 0) as return_amount + from `tabGL Entry` + where against_voucher_type = 'Employee Advance' + and voucher_type != 'Expense Claim' + and against_voucher = %s + and party_type = 'Employee' + and party = %s + """, (self.name, self.employee), as_dict=1)[0].return_amount + if flt(paid_amount) > self.advance_amount: frappe.throw(_("Row {0}# Paid Amount cannot be greater than requested advance amount"), EmployeeAdvanceOverPayment) + if flt(return_amount) > self.paid_amount - self.claimed_amount: + frappe.throw(_("Return amount cannot be greater unclaimed amount")) + self.db_set("paid_amount", paid_amount) + self.db_set("return_amount", return_amount) self.set_status() frappe.db.set_value("Employee Advance", self.name , "status", self.status) @@ -88,8 +103,6 @@ def get_due_advance_amount(employee, posting_date): @frappe.whitelist() def make_bank_entry(dt, dn): - from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account - doc = frappe.get_doc(dt, dn) payment_account = get_default_bank_cash_account(doc.company, account_type="Cash", mode_of_payment=doc.mode_of_payment) @@ -118,3 +131,34 @@ def make_bank_entry(dt, dn): }) return je.as_dict() + +@frappe.whitelist() +def make_return_entry(employee, company, employee_advance_name, + return_amount, advance_account, mode_of_payment=None): + return_account = get_default_bank_cash_account(company, account_type='Cash', mode_of_payment = mode_of_payment) + je = frappe.new_doc('Journal Entry') + je.posting_date = nowdate() + je.voucher_type = 'Bank Entry' + je.company = company + je.remark = 'Return against Employee Advance: ' + employee_advance_name + + je.append('accounts', { + 'account': advance_account, + 'credit_in_account_currency': return_amount, + 'reference_type': 'Employee Advance', + 'reference_name': employee_advance_name, + 'party_type': 'Employee', + 'party': employee, + 'is_advance': 'Yes' + }) + + je.append("accounts", { + "account": return_account.account, + "debit_in_account_currency": return_amount, + "account_currency": return_account.account_currency, + "account_type": return_account.account_type + }) + + return je.as_dict() + + diff --git a/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py b/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py new file mode 100644 index 00000000000..c3b4a3a8894 --- /dev/null +++ b/erpnext/hr/doctype/employee_advance/employee_advance_dashboard.py @@ -0,0 +1,19 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'employee_advance', + 'non_standard_fieldnames': { + 'Payment Entry': 'reference_name', + 'Journal Entry': 'reference_name' + }, + 'transactions': [ + { + 'items': ['Expense Claim'] + }, + { + 'items': ['Payment Entry', 'Journal Entry'] + } + ] + } diff --git a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js index 22ba5ad473e..3205a92b1b6 100644 --- a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js +++ b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.js @@ -124,8 +124,10 @@ erpnext.EmployeeSelector = Class.extend({ var mark_employee_toolbar = $('
\ \ - \ -
') + \ + \ + \ +
'); employee_toolbar.find(".btn-add") .html(__('Check all')) @@ -224,6 +226,31 @@ erpnext.EmployeeSelector = Class.extend({ }); + mark_employee_toolbar.find(".btn-mark-work-from-home") + .html(__('Mark Work From Home')) + .on("click", function() { + var employee_work_from_home = []; + $(me.wrapper).find('input[type="checkbox"]').each(function(i, check) { + if($(check).is(":checked")) { + employee_work_from_home.push(employee[i]); + } + }); + frappe.call({ + method: "erpnext.hr.doctype.employee_attendance_tool.employee_attendance_tool.mark_employee_attendance", + args:{ + "employee_list":employee_work_from_home, + "status":"Work From Home", + "date":frm.doc.date, + "company":frm.doc.company + }, + + callback: function(r) { + erpnext.employee_attendance_tool.load_employees(frm); + + } + }); + }); + var row; $.each(employee, function(i, m) { if (i===0 || (i % 4) === 0) { diff --git a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py index 32fcee1abe4..16c1a32b9b5 100644 --- a/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py +++ b/erpnext/hr/doctype/employee_attendance_tool/employee_attendance_tool.py @@ -6,6 +6,7 @@ from __future__ import unicode_literals import frappe import json from frappe.model.document import Document +from frappe.utils import getdate class EmployeeAttendanceTool(Document): @@ -43,17 +44,26 @@ def get_employees(date, department = None, branch = None, company = None): @frappe.whitelist() def mark_employee_attendance(employee_list, status, date, leave_type=None, company=None): + employee_list = json.loads(employee_list) for employee in employee_list: - attendance = frappe.new_doc("Attendance") - attendance.employee = employee['employee'] - attendance.employee_name = employee['employee_name'] - attendance.attendance_date = date - attendance.status = status + if status == "On Leave" and leave_type: - attendance.leave_type = leave_type - if company: - attendance.company = company + leave_type = leave_type else: - attendance.company = frappe.db.get_value("Employee", employee['employee'], "Company") + leave_type = None + + if not company: + company = frappe.db.get_value("Employee", employee['employee'], "Company") + + attendance=frappe.get_doc(dict( + doctype='Attendance', + employee=employee.get('employee'), + employee_name=employee.get('employee_name'), + attendance_date=getdate(date), + status=status, + leave_type=leave_type, + company=company + )) + attendance.insert() attendance.submit() diff --git a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py b/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py index abb82f2cdd1..3a12c9c9f9c 100644 --- a/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py +++ b/erpnext/hr/doctype/employee_benefit_claim/employee_benefit_claim.py @@ -84,7 +84,7 @@ def get_benefit_pro_rata_ratio_amount(employee, on_date, sal_struct): pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component", sal_struct_row.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"]) except TypeError: # show the error in tests? - frappe.throw(_("Unable to find Salary Component {0}".format(sal_struct_row.salary_component))) + frappe.throw(_("Unable to find Salary Component {0}").format(sal_struct_row.salary_component)) if sal_struct_row.is_flexible_benefit == 1 and pay_against_benefit_claim != 1: total_pro_rata_max += max_benefit_amount if total_pro_rata_max > 0: diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.js b/erpnext/hr/doctype/employee_checkin/employee_checkin.js index f11cc9b2522..c2403ca2bd9 100644 --- a/erpnext/hr/doctype/employee_checkin/employee_checkin.js +++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.js @@ -2,7 +2,9 @@ // For license information, please see license.txt frappe.ui.form.on('Employee Checkin', { - // refresh: function(frm) { - - // } + setup: (frm) => { + if(!frm.doc.time) { + frm.set_value("time", frappe.datetime.now_datetime()); + } + } }); diff --git a/erpnext/hr/doctype/employee_checkin/employee_checkin.json b/erpnext/hr/doctype/employee_checkin/employee_checkin.json index 08fa4afa5cb..75f699751b8 100644 --- a/erpnext/hr/doctype/employee_checkin/employee_checkin.json +++ b/erpnext/hr/doctype/employee_checkin/employee_checkin.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "autoname": "EMP-CKIN-.MM.-.YYYY.-.######", "creation": "2019-06-10 11:56:34.536413", @@ -23,7 +24,6 @@ { "fieldname": "employee", "fieldtype": "Link", - "in_list_view": 1, "label": "Employee", "options": "Employee", "reqd": 1 @@ -32,14 +32,17 @@ "fetch_from": "employee.employee_name", "fieldname": "employee_name", "fieldtype": "Data", + "in_list_view": 1, "label": "Employee Name", "read_only": 1 }, { "fieldname": "log_type", "fieldtype": "Select", + "in_list_view": 1, "label": "Log Type", - "options": "\nIN\nOUT" + "options": "\nIN\nOUT", + "reqd": 1 }, { "fieldname": "shift", @@ -58,6 +61,7 @@ "fieldtype": "Datetime", "in_list_view": 1, "label": "Time", + "permlevel": 1, "reqd": 1 }, { @@ -103,7 +107,8 @@ "label": "Shift Actual End" } ], - "modified": "2019-07-23 23:47:33.975263", + "links": [], + "modified": "2020-01-23 04:57:42.551355", "modified_by": "Administrator", "module": "HR", "name": "Employee Checkin", @@ -147,9 +152,58 @@ "role": "HR User", "share": 1, "write": 1 + }, + { + "create": 1, + "delete": 1, + "read": 1, + "role": "Employee", + "write": 1 + }, + { + "delete": 1, + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "write": 1 + }, + { + "delete": 1, + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "write": 1 + }, + { + "delete": 1, + "email": 1, + "export": 1, + "permlevel": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "write": 1 + }, + { + "permlevel": 1, + "read": 1, + "role": "Employee" } ], "sort_field": "modified", "sort_order": "ASC", + "title_field": "employee_name", "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/hr/doctype/employee_incentive/employee_incentive.json b/erpnext/hr/doctype/employee_incentive/employee_incentive.json index 3bc772cfa68..ce8e1ea230a 100644 --- a/erpnext/hr/doctype/employee_incentive/employee_incentive.json +++ b/erpnext/hr/doctype/employee_incentive/employee_incentive.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "HR-EINV-.YY.-.MM.-.#####", "creation": "2018-04-13 16:13:43.404546", "doctype": "DocType", @@ -7,13 +8,13 @@ "field_order": [ "employee", "incentive_amount", + "employee_name", + "additional_salary", + "column_break_5", "payroll_date", "salary_component", - "amended_from", - "column_break_5", - "employee_name", "department", - "additional_salary" + "amended_from" ], "fields": [ { @@ -81,7 +82,8 @@ } ], "is_submittable": 1, - "modified": "2019-09-03 16:48:16.822252", + "links": [], + "modified": "2019-12-12 13:24:44.761540", "modified_by": "Administrator", "module": "HR", "name": "Employee Incentive", diff --git a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js index 996dcdf8484..c1285675e5c 100644 --- a/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js +++ b/erpnext/hr/doctype/employee_onboarding/employee_onboarding.js @@ -7,6 +7,14 @@ frappe.ui.form.on('Employee Onboarding', { frm.add_fetch("employee_onboarding_template", "department", "department"); frm.add_fetch("employee_onboarding_template", "designation", "designation"); frm.add_fetch("employee_onboarding_template", "employee_grade", "employee_grade"); + + frm.set_query('job_offer', function () { + return { + filters: { + 'job_applicant': frm.doc.job_applicant + } + }; + }); }, refresh: function(frm) { diff --git a/erpnext/hr/doctype/employee_skill_map/employee_skill_map.json b/erpnext/hr/doctype/employee_skill_map/employee_skill_map.json index 624145f23a9..21e25132a16 100644 --- a/erpnext/hr/doctype/employee_skill_map/employee_skill_map.json +++ b/erpnext/hr/doctype/employee_skill_map/employee_skill_map.json @@ -1,298 +1,88 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, + "actions": [], "autoname": "field:employee", - "beta": 0, "creation": "2019-04-16 10:07:48.303426", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", - "editable_grid": 0, "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "column_break_3", + "designation", + "skills_section", + "employee_skills", + "trainings_section", + "trainings" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "employee", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Employee", - "length": 0, - "no_copy": 0, "options": "Employee", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, "unique": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "employee.employee_name", - "fetch_if_empty": 0, "fieldname": "employee_name", "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Employee Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Employee Name" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fetch_from": "employee.designation", - "fetch_if_empty": 0, "fieldname": "designation", "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Designation", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Designation" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "skills_section", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Skills", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Skills" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "employee_skills", "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Employee Skills", - "length": 0, - "no_copy": 0, - "options": "Employee Skill", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Employee Skill" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "trainings_section", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Trainings", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Trainings" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, "fieldname": "trainings", "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Trainings", - "length": 0, - "no_copy": 0, - "options": "Employee Training", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Employee Training" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" } ], - "has_web_view": 0, - "hide_toolbar": 0, - "idx": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-04-16 16:16:40.058429", + "links": [], + "modified": "2019-12-16 11:31:09.916893", "modified_by": "Administrator", "module": "HR", "name": "Employee Skill Map", - "name_case": "", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "System Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 } ], "quick_entry": 1, - "read_only": 0, - "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "ASC", - "title_field": "employee_name", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "title_field": "employee_name" } \ No newline at end of file diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.js b/erpnext/hr/doctype/expense_claim/expense_claim.js index 6d3a28e5e24..88f3865434b 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.js +++ b/erpnext/hr/doctype/expense_claim/expense_claim.js @@ -42,12 +42,6 @@ cur_frm.cscript.onload = function(doc) { cur_frm.set_value("posting_date", frappe.datetime.get_today()); cur_frm.cscript.clear_sanctioned(doc); } - - cur_frm.fields_dict.employee.get_query = function() { - return { - query: "erpnext.controllers.queries.employee_query" - }; - }; }; cur_frm.cscript.clear_sanctioned = function(doc) { @@ -119,7 +113,7 @@ cur_frm.cscript.calculate_total_amount = function(doc,cdt,cdn){ }; erpnext.expense_claim = { - set_title :function(frm) { + set_title: function(frm) { if (!frm.doc.task) { frm.set_value("title", frm.doc.employee_name); } @@ -131,20 +125,20 @@ erpnext.expense_claim = { frappe.ui.form.on("Expense Claim", { setup: function(frm) { - frm.trigger("set_query_for_cost_center"); - frm.trigger("set_query_for_payable_account"); frm.add_fetch("company", "cost_center", "cost_center"); frm.add_fetch("company", "default_expense_claim_payable_account", "payable_account"); - frm.set_query("employee_advance", "advances", function(doc) { + + frm.set_query("employee_advance", "advances", function() { return { filters: [ ['docstatus', '=', 1], - ['employee', '=', doc.employee], + ['employee', '=', frm.doc.employee], ['paid_amount', '>', 0], ['paid_amount', '>', 'claimed_amount'] ] }; }); + frm.set_query("expense_approver", function() { return { query: "erpnext.hr.doctype.department_approver.department_approver.get_approvers", @@ -154,14 +148,49 @@ frappe.ui.form.on("Expense Claim", { } }; }); - frm.set_query("account_head", "taxes", function(doc) { + + frm.set_query("account_head", "taxes", function() { return { filters: [ - ['company', '=', doc.company], + ['company', '=', frm.doc.company], ['account_type', 'in', ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation"]] ] }; }); + + frm.set_query("cost_center", "expenses", function() { + return { + filters: { + "company": frm.doc.company, + "is_group": 0 + } + }; + }); + + frm.set_query("payable_account", function() { + return { + filters: { + "report_type": "Balance Sheet", + "account_type": "Payable", + "company": frm.doc.company, + "is_group": 0 + } + }; + }); + + frm.set_query("task", function() { + return { + filters: { + 'project': frm.doc.project + } + }; + }); + + frm.set_query("employee", function() { + return { + query: "erpnext.controllers.queries.employee_query" + }; + }); }, onload: function(frm) { @@ -208,6 +237,25 @@ frappe.ui.form.on("Expense Claim", { frm.refresh_fields(); }, + grand_total: function(frm) { + frm.trigger("update_employee_advance_claimed_amount"); + }, + + update_employee_advance_claimed_amount: function(frm) { + console.log("update_employee_advance_claimed_amount") + let amount_to_be_allocated = frm.doc.grand_total; + $.each(frm.doc.advances || [], function(i, advance){ + if (amount_to_be_allocated >= advance.unclaimed_amount){ + advance.allocated_amount = frm.doc.advances[i].unclaimed_amount; + amount_to_be_allocated -= advance.allocated_amount; + } else { + advance.allocated_amount = amount_to_be_allocated; + amount_to_be_allocated = 0; + } + frm.refresh_field("advances"); + }); + }, + make_payment_entry: function(frm) { var method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry"; if(frm.doc.__onload && frm.doc.__onload.make_payment_via_journal_entry) { @@ -226,30 +274,6 @@ frappe.ui.form.on("Expense Claim", { }); }, - set_query_for_cost_center: function(frm) { - frm.fields_dict["cost_center"].get_query = function() { - return { - filters: { - "company": frm.doc.company, - "is_group": 0 - } - }; - }; - }, - - set_query_for_payable_account: function(frm) { - frm.fields_dict["payable_account"].get_query = function() { - return { - filters: { - "report_type": "Balance Sheet", - "account_type": "Payable", - "company": frm.doc.company, - "is_group": 0 - } - }; - }; - }, - is_paid: function(frm) { frm.trigger("toggle_fields"); }, @@ -277,6 +301,7 @@ frappe.ui.form.on("Expense Claim", { doc: frm.doc, callback: () => { refresh_field("taxes"); + frm.trigger("update_employee_advance_claimed_amount"); } }); } @@ -300,7 +325,7 @@ frappe.ui.form.on("Expense Claim", { row.advance_account = d.advance_account; row.advance_paid = d.paid_amount; row.unclaimed_amount = flt(d.paid_amount) - flt(d.claimed_amount); - row.allocated_amount = flt(d.paid_amount) - flt(d.claimed_amount); + row.allocated_amount = 0; }); refresh_field("advances"); } @@ -311,18 +336,21 @@ frappe.ui.form.on("Expense Claim", { }); frappe.ui.form.on("Expense Claim Detail", { + expenses_add: function(frm, cdt, cdn) { + var row = frappe.get_doc(cdt, cdn); + frm.script_manager.copy_from_first_row("expenses", row, ["cost_center"]); + }, amount: function(frm, cdt, cdn) { var child = locals[cdt][cdn]; - var doc = frm.doc; frappe.model.set_value(cdt, cdn, 'sanctioned_amount', child.amount); - cur_frm.cscript.calculate_total(doc,cdt,cdn); }, sanctioned_amount: function(frm, cdt, cdn) { - var doc = frm.doc; - cur_frm.cscript.calculate_total(doc,cdt,cdn); + cur_frm.cscript.calculate_total(frm.doc, cdt, cdn); frm.trigger("get_taxes"); - frm.trigger("calculate_grand_total"); + }, + cost_center: function(frm, cdt, cdn) { + erpnext.utils.copy_value_in_all_rows(frm.doc, cdt, cdn, "expenses", "cost_center"); } }); @@ -393,12 +421,4 @@ frappe.ui.form.on("Expense Taxes and Charges", { tax_amount: function(frm, cdt, cdn) { frm.trigger("calculate_total_tax", cdt, cdn); } -}); - -cur_frm.fields_dict['task'].get_query = function(doc) { - return { - filters:{ - 'project': doc.project - } - }; -}; +}); \ No newline at end of file diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.json b/erpnext/hr/doctype/expense_claim/expense_claim.json index 4e2778f48d7..96baaab5950 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.json +++ b/erpnext/hr/doctype/expense_claim/expense_claim.json @@ -1,435 +1,438 @@ { - "allow_import": 1, - "autoname": "naming_series:", - "creation": "2013-01-10 16:34:14", - "doctype": "DocType", - "document_type": "Setup", - "engine": "InnoDB", - "field_order": [ - "naming_series", - "employee", - "employee_name", - "department", - "column_break_5", - "expense_approver", - "approval_status", - "is_paid", - "expense_details", - "expenses", - "sb1", - "taxes", - "transactions_section", - "total_sanctioned_amount", - "total_taxes_and_charges", - "total_advance_amount", - "column_break_17", - "grand_total", - "total_claimed_amount", - "total_amount_reimbursed", - "section_break_16", - "posting_date", - "vehicle_log", - "task", - "cb1", - "remark", - "title", - "email_id", - "accounting_details", - "company", - "mode_of_payment", - "clearance_date", - "column_break_24", - "payable_account", - "accounting_dimensions_section", - "project", - "dimension_col_break", - "cost_center", - "more_details", - "status", - "amended_from", - "advance_payments", - "advances" - ], - "fields": [ - { - "fieldname": "naming_series", - "fieldtype": "Select", - "label": "Series", - "no_copy": 1, - "options": "HR-EXP-.YYYY.-", - "print_hide": 1, - "reqd": 1, - "set_only_once": 1 - }, - { - "fieldname": "employee", - "fieldtype": "Link", - "in_global_search": 1, - "label": "From Employee", - "oldfieldname": "employee", - "oldfieldtype": "Link", - "options": "Employee", - "reqd": 1, - "search_index": 1 - }, - { - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "in_global_search": 1, - "label": "Employee Name", - "oldfieldname": "employee_name", - "oldfieldtype": "Data", - "read_only": 1, - "width": "150px" - }, - { - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "label": "Department", - "options": "Department", - "read_only": 1 - }, - { - "fieldname": "column_break_5", - "fieldtype": "Column Break" - }, - { - "fieldname": "expense_approver", - "fieldtype": "Link", - "label": "Expense Approver", - "options": "User" - }, - { - "default": "Draft", - "fieldname": "approval_status", - "fieldtype": "Select", - "label": "Approval Status", - "no_copy": 1, - "options": "Draft\nApproved\nRejected", - "search_index": 1 - }, - { - "fieldname": "total_claimed_amount", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Total Claimed Amount", - "no_copy": 1, - "oldfieldname": "total_claimed_amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "read_only": 1, - "width": "160px" - }, - { - "fieldname": "total_sanctioned_amount", - "fieldtype": "Currency", - "label": "Total Sanctioned Amount", - "no_copy": 1, - "oldfieldname": "total_sanctioned_amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "read_only": 1, - "width": "160px" - }, - { - "default": "0", - "depends_on": "eval:(doc.docstatus==0 || doc.is_paid)", - "fieldname": "is_paid", - "fieldtype": "Check", - "label": "Is Paid" - }, - { - "fieldname": "expense_details", - "fieldtype": "Section Break", - "oldfieldtype": "Section Break" - }, - { - "fieldname": "expenses", - "fieldtype": "Table", - "label": "Expenses", - "oldfieldname": "expense_voucher_details", - "oldfieldtype": "Table", - "options": "Expense Claim Detail", - "reqd": 1 - }, - { - "fieldname": "sb1", - "fieldtype": "Section Break", - "options": "Simple" - }, - { - "default": "Today", - "fieldname": "posting_date", - "fieldtype": "Date", - "label": "Posting Date", - "oldfieldname": "posting_date", - "oldfieldtype": "Date", - "reqd": 1 - }, - { - "fieldname": "vehicle_log", - "fieldtype": "Link", - "label": "Vehicle Log", - "options": "Vehicle Log", - "read_only": 1 - }, - { - "fieldname": "project", - "fieldtype": "Link", - "label": "Project", - "options": "Project" - }, - { - "fieldname": "task", - "fieldtype": "Link", - "label": "Task", - "options": "Task", - "remember_last_selected_value": 1 - }, - { - "fieldname": "cb1", - "fieldtype": "Column Break" - }, - { - "fieldname": "total_amount_reimbursed", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Total Amount Reimbursed", - "no_copy": 1, - "options": "Company:company:default_currency", - "read_only": 1 - }, - { - "fieldname": "remark", - "fieldtype": "Small Text", - "label": "Remark", - "no_copy": 1, - "oldfieldname": "remark", - "oldfieldtype": "Small Text" - }, - { - "allow_on_submit": 1, - "default": "{employee_name}", - "fieldname": "title", - "fieldtype": "Data", - "hidden": 1, - "label": "Title", - "no_copy": 1 - }, - { - "fieldname": "email_id", - "fieldtype": "Data", - "hidden": 1, - "label": "Employees Email Id", - "oldfieldname": "email_id", - "oldfieldtype": "Data", - "print_hide": 1 - }, - { - "fieldname": "accounting_details", - "fieldtype": "Section Break", - "label": "Accounting Details" - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "oldfieldname": "company", - "oldfieldtype": "Link", - "options": "Company", - "remember_last_selected_value": 1, - "reqd": 1 - }, - { - "depends_on": "is_paid", - "fieldname": "mode_of_payment", - "fieldtype": "Link", - "label": "Mode of Payment", - "options": "Mode of Payment" - }, - { - "fieldname": "clearance_date", - "fieldtype": "Date", - "label": "Clearance Date" - }, - { - "fieldname": "column_break_24", - "fieldtype": "Column Break" - }, - { - "fieldname": "payable_account", - "fieldtype": "Link", - "label": "Payable Account", - "options": "Account" - }, - { - "fieldname": "cost_center", - "fieldtype": "Link", - "label": "Cost Center", - "options": "Cost Center" - }, - { - "collapsible": 1, - "fieldname": "more_details", - "fieldtype": "Section Break", - "label": "More Details" - }, - { - "default": "Draft", - "fieldname": "status", - "fieldtype": "Select", - "in_list_view": 1, - "label": "Status", - "no_copy": 1, - "options": "Draft\nPaid\nUnpaid\nRejected\nSubmitted\nCancelled", - "print_hide": 1, - "read_only": 1 - }, - { - "fieldname": "amended_from", - "fieldtype": "Link", - "ignore_user_permissions": 1, - "label": "Amended From", - "no_copy": 1, - "oldfieldname": "amended_from", - "oldfieldtype": "Data", - "options": "Expense Claim", - "print_hide": 1, - "read_only": 1, - "report_hide": 1, - "width": "160px" - }, - { - "fieldname": "advance_payments", - "fieldtype": "Section Break", - "label": "Advance Payments" - }, - { - "fieldname": "advances", - "fieldtype": "Table", - "label": "Advances", - "options": "Expense Claim Advance" - }, - { - "fieldname": "total_advance_amount", - "fieldtype": "Currency", - "label": "Total Advance Amount", - "options": "Company:company:default_currency", - "read_only": 1 - }, - { - "fieldname": "accounting_dimensions_section", - "fieldtype": "Section Break", - "label": "Accounting Dimensions" - }, - { - "fieldname": "dimension_col_break", - "fieldtype": "Column Break" - }, - { - "fieldname": "taxes", - "fieldtype": "Table", - "label": "Expense Taxes and Charges", - "options": "Expense Taxes and Charges" - }, - { - "fieldname": "section_break_16", - "fieldtype": "Section Break" - }, - { - "fieldname": "transactions_section", - "fieldtype": "Section Break" - }, - { - "fieldname": "grand_total", - "fieldtype": "Currency", - "in_list_view": 1, - "label": "Grand Total", - "options": "Company:company:default_currency", - "read_only": 1 - }, - { - "fieldname": "column_break_17", - "fieldtype": "Column Break" - }, - { - "fieldname": "total_taxes_and_charges", - "fieldtype": "Currency", - "label": "Total Taxes and Charges", - "options": "Company:company:default_currency", - "read_only": 1 - } - ], - "icon": "fa fa-money", - "idx": 1, - "is_submittable": 1, - "modified": "2019-06-26 18:05:52.530462", - "modified_by": "Administrator", - "module": "HR", - "name": "Expense Claim", - "name_case": "Title Case", - "owner": "harshada@webnotestech.com", - "permissions": [ - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "create": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "share": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "Expense Approver", - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "share": 1, - "submit": 1, - "write": 1 - } - ], - "search_fields": "employee,employee_name", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "timeline_field": "employee", - "title_field": "title" - } \ No newline at end of file + "actions": [], + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2013-01-10 16:34:14", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "naming_series", + "employee", + "employee_name", + "department", + "column_break_5", + "expense_approver", + "approval_status", + "is_paid", + "expense_details", + "expenses", + "sb1", + "taxes", + "transactions_section", + "total_sanctioned_amount", + "total_taxes_and_charges", + "total_advance_amount", + "column_break_17", + "grand_total", + "total_claimed_amount", + "total_amount_reimbursed", + "section_break_16", + "posting_date", + "vehicle_log", + "task", + "cb1", + "remark", + "title", + "email_id", + "accounting_details", + "company", + "mode_of_payment", + "clearance_date", + "column_break_24", + "payable_account", + "accounting_dimensions_section", + "project", + "dimension_col_break", + "cost_center", + "more_details", + "status", + "amended_from", + "advance_payments", + "advances" + ], + "fields": [ + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "options": "HR-EXP-.YYYY.-", + "print_hide": 1, + "reqd": 1, + "set_only_once": 1 + }, + { + "fieldname": "employee", + "fieldtype": "Link", + "in_global_search": 1, + "label": "From Employee", + "oldfieldname": "employee", + "oldfieldtype": "Link", + "options": "Employee", + "reqd": 1, + "search_index": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Employee Name", + "oldfieldname": "employee_name", + "oldfieldtype": "Data", + "read_only": 1, + "width": "150px" + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" + }, + { + "fieldname": "expense_approver", + "fieldtype": "Link", + "label": "Expense Approver", + "options": "User" + }, + { + "default": "Draft", + "fieldname": "approval_status", + "fieldtype": "Select", + "label": "Approval Status", + "no_copy": 1, + "options": "Draft\nApproved\nRejected", + "search_index": 1 + }, + { + "fieldname": "total_claimed_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Total Claimed Amount", + "no_copy": 1, + "oldfieldname": "total_claimed_amount", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "read_only": 1, + "width": "160px" + }, + { + "fieldname": "total_sanctioned_amount", + "fieldtype": "Currency", + "label": "Total Sanctioned Amount", + "no_copy": 1, + "oldfieldname": "total_sanctioned_amount", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "read_only": 1, + "width": "160px" + }, + { + "default": "0", + "depends_on": "eval:(doc.docstatus==0 || doc.is_paid)", + "fieldname": "is_paid", + "fieldtype": "Check", + "label": "Is Paid" + }, + { + "fieldname": "expense_details", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break" + }, + { + "fieldname": "expenses", + "fieldtype": "Table", + "label": "Expenses", + "oldfieldname": "expense_voucher_details", + "oldfieldtype": "Table", + "options": "Expense Claim Detail", + "reqd": 1 + }, + { + "fieldname": "sb1", + "fieldtype": "Section Break", + "options": "Simple" + }, + { + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting Date", + "oldfieldname": "posting_date", + "oldfieldtype": "Date", + "reqd": 1 + }, + { + "fieldname": "vehicle_log", + "fieldtype": "Link", + "label": "Vehicle Log", + "options": "Vehicle Log", + "read_only": 1 + }, + { + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" + }, + { + "fieldname": "task", + "fieldtype": "Link", + "label": "Task", + "options": "Task", + "remember_last_selected_value": 1 + }, + { + "fieldname": "cb1", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_amount_reimbursed", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Total Amount Reimbursed", + "no_copy": 1, + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "remark", + "fieldtype": "Small Text", + "label": "Remark", + "no_copy": 1, + "oldfieldname": "remark", + "oldfieldtype": "Small Text" + }, + { + "allow_on_submit": 1, + "default": "{employee_name}", + "fieldname": "title", + "fieldtype": "Data", + "hidden": 1, + "label": "Title", + "no_copy": 1 + }, + { + "fieldname": "email_id", + "fieldtype": "Data", + "hidden": 1, + "label": "Employees Email Id", + "oldfieldname": "email_id", + "oldfieldtype": "Data", + "print_hide": 1 + }, + { + "fieldname": "accounting_details", + "fieldtype": "Section Break", + "label": "Accounting Details" + }, + { + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "oldfieldname": "company", + "oldfieldtype": "Link", + "options": "Company", + "remember_last_selected_value": 1, + "reqd": 1 + }, + { + "depends_on": "is_paid", + "fieldname": "mode_of_payment", + "fieldtype": "Link", + "label": "Mode of Payment", + "options": "Mode of Payment" + }, + { + "fieldname": "clearance_date", + "fieldtype": "Date", + "label": "Clearance Date" + }, + { + "fieldname": "column_break_24", + "fieldtype": "Column Break" + }, + { + "fieldname": "payable_account", + "fieldtype": "Link", + "label": "Payable Account", + "options": "Account", + "reqd": 1 + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" + }, + { + "collapsible": 1, + "fieldname": "more_details", + "fieldtype": "Section Break", + "label": "More Details" + }, + { + "default": "Draft", + "fieldname": "status", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Status", + "no_copy": 1, + "options": "Draft\nPaid\nUnpaid\nRejected\nSubmitted\nCancelled", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Amended From", + "no_copy": 1, + "oldfieldname": "amended_from", + "oldfieldtype": "Data", + "options": "Expense Claim", + "print_hide": 1, + "read_only": 1, + "report_hide": 1, + "width": "160px" + }, + { + "fieldname": "advance_payments", + "fieldtype": "Section Break", + "label": "Advance Payments" + }, + { + "fieldname": "advances", + "fieldtype": "Table", + "label": "Advances", + "options": "Expense Claim Advance" + }, + { + "fieldname": "total_advance_amount", + "fieldtype": "Currency", + "label": "Total Advance Amount", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "taxes", + "fieldtype": "Table", + "label": "Expense Taxes and Charges", + "options": "Expense Taxes and Charges" + }, + { + "fieldname": "section_break_16", + "fieldtype": "Section Break" + }, + { + "fieldname": "transactions_section", + "fieldtype": "Section Break" + }, + { + "fieldname": "grand_total", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Grand Total", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_taxes_and_charges", + "fieldtype": "Currency", + "label": "Total Taxes and Charges", + "options": "Company:company:default_currency", + "read_only": 1 + } + ], + "icon": "fa fa-money", + "idx": 1, + "is_submittable": 1, + "links": [], + "modified": "2019-12-14 23:52:05.388458", + "modified_by": "Administrator", + "module": "HR", + "name": "Expense Claim", + "name_case": "Title Case", + "owner": "harshada@webnotestech.com", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Expense Approver", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "search_fields": "employee,employee_name", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "timeline_field": "employee", + "title_field": "title" +} \ No newline at end of file diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py index caeb2dd9466..fe8afdf8734 100644 --- a/erpnext/hr/doctype/expense_claim/expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/expense_claim.py @@ -43,9 +43,9 @@ class ExpenseClaim(AccountsController): }[cstr(self.docstatus or 0)] paid_amount = flt(self.total_amount_reimbursed) + flt(self.total_advance_amount) - precision = self.precision("total_sanctioned_amount") + precision = self.precision("grand_total") if (self.is_paid or (flt(self.total_sanctioned_amount) > 0 - and flt(self.total_sanctioned_amount, precision) == flt(paid_amount, precision))) \ + and flt(self.grand_total, precision) == flt(paid_amount, precision))) \ and self.docstatus == 1 and self.approval_status == 'Approved': self.status = "Paid" elif flt(self.total_sanctioned_amount) > 0 and self.docstatus == 1 and self.approval_status == 'Approved': @@ -127,7 +127,7 @@ class ExpenseClaim(AccountsController): "debit": data.sanctioned_amount, "debit_in_account_currency": data.sanctioned_amount, "against": self.employee, - "cost_center": self.cost_center + "cost_center": data.cost_center }) ) @@ -140,10 +140,11 @@ class ExpenseClaim(AccountsController): "against": ",".join([d.default_account for d in self.expenses]), "party_type": "Employee", "party": self.employee, - "against_voucher_type": self.doctype, - "against_voucher": self.name + "against_voucher_type": "Employee Advance", + "against_voucher": data.employee_advance }) ) + self.add_tax_gl_entries(gl_entry) if self.is_paid and self.grand_total: @@ -189,11 +190,9 @@ class ExpenseClaim(AccountsController): ) def validate_account_details(self): - if not self.cost_center: - frappe.throw(_("Cost center is required to book an expense claim")) - - if not self.payable_account: - frappe.throw(_("Please set default payable account for the company {0}").format(getlink("Company",self.company))) + for data in self.expenses: + if not data.cost_center: + frappe.throw(_("Cost center is required to book an expense claim")) if self.is_paid: if not self.mode_of_payment: @@ -245,6 +244,7 @@ class ExpenseClaim(AccountsController): precision = self.precision("total_advance_amount") if flt(self.total_advance_amount, precision) > flt(self.total_claimed_amount, precision): frappe.throw(_("Total advance amount cannot be greater than total claimed amount")) + if self.total_sanctioned_amount \ and flt(self.total_advance_amount, precision) > flt(self.total_sanctioned_amount, precision): frappe.throw(_("Total advance amount cannot be greater than total sanctioned amount")) @@ -323,7 +323,7 @@ def get_expense_claim_account(expense_claim_type, company): @frappe.whitelist() def get_advances(employee, advance_id=None): if not advance_id: - condition = 'docstatus=1 and employee={0} and paid_amount > 0 and paid_amount > claimed_amount'.format(frappe.db.escape(employee)) + condition = 'docstatus=1 and employee={0} and paid_amount > 0 and paid_amount > claimed_amount + return_amount'.format(frappe.db.escape(employee)) else: condition = 'name={0}'.format(frappe.db.escape(advance_id)) diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py index b559dfd81d1..6e97f0513d6 100644 --- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py @@ -126,7 +126,7 @@ def generate_taxes(): def make_expense_claim(payable_account, amount, sanctioned_amount, company, account, project=None, task_name=None, do_not_submit=False, taxes=None): employee = frappe.db.get_value("Employee", {"status": "Active"}) - currency = frappe.db.get_value('Company', company, 'default_currency') + currency, cost_center = frappe.db.get_value('Company', company, ['default_currency', 'cost_center']) expense_claim = { "doctype": "Expense Claim", "employee": employee, @@ -134,12 +134,15 @@ def make_expense_claim(payable_account, amount, sanctioned_amount, company, acco "approval_status": "Approved", "company": company, 'currency': currency, - "expenses": - [{"expense_type": "Travel", + "expenses": [{ + "expense_type": "Travel", "default_account": account, - 'currency': currency, + "currency": currency, "amount": amount, - "sanctioned_amount": sanctioned_amount}]} + "sanctioned_amount": sanctioned_amount, + "cost_center": cost_center + }] + } if taxes: expense_claim.update(taxes) diff --git a/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.json b/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.json index b5e4fd16f8e..45509257c11 100644 --- a/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.json +++ b/erpnext/hr/doctype/expense_claim_advance/expense_claim_advance.json @@ -1,238 +1,99 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2017-10-09 16:53:26.410762", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", + "creation": "2017-10-09 16:53:26.410762", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee_advance", + "posting_date", + "advance_paid", + "column_break_4", + "unclaimed_amount", + "allocated_amount", + "advance_account" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "employee_advance", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee Advance", - "length": 0, - "no_copy": 1, - "oldfieldname": "journal_voucher", - "oldfieldtype": "Link", - "options": "Employee Advance", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "250px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0, + "columns": 2, + "fieldname": "employee_advance", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee Advance", + "no_copy": 1, + "oldfieldname": "journal_voucher", + "oldfieldtype": "Link", + "options": "Employee Advance", + "print_width": "250px", + "reqd": 1, "width": "250px" - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "posting_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Posting Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "posting_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Posting Date", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "advance_paid", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Advance Paid", - "length": 0, - "no_copy": 0, - "options": "Company:company.default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "advance_paid", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Advance Paid", + "options": "Company:company:default_currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "unclaimed_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Unclaimed amount", - "length": 0, - "no_copy": 1, - "oldfieldname": "advance_amount", - "oldfieldtype": "Currency", - "options": "Company:company.default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "120px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0, + "columns": 2, + "fieldname": "unclaimed_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Unclaimed amount", + "no_copy": 1, + "oldfieldname": "advance_amount", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_width": "120px", + "read_only": 1, + "reqd": 1, "width": "120px" - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "allocated_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Allocated amount", - "length": 0, - "no_copy": 1, - "oldfieldname": "allocated_amount", - "oldfieldtype": "Currency", - "options": "Company:company.default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "120px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0, + "columns": 2, + "fieldname": "allocated_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Allocated amount", + "no_copy": 1, + "oldfieldname": "allocated_amount", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_width": "120px", "width": "120px" - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "advance_account", - "fieldtype": "Link", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Advance Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "advance_account", + "fieldtype": "Link", + "hidden": 1, + "label": "Advance Account", + "options": "Account" + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2017-10-09 19:59:48.818139", - "modified_by": "Administrator", - "module": "HR", - "name": "Expense Claim Advance", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 + ], + "istable": 1, + "links": [], + "modified": "2019-12-17 13:53:22.111766", + "modified_by": "Administrator", + "module": "HR", + "name": "Expense Claim Advance", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json index b23fb6af0fe..16e9eef9174 100644 --- a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json +++ b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json @@ -1,378 +1,120 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, + "actions": [], "creation": "2013-02-22 01:27:46", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "expense_date", + "column_break_2", + "expense_type", + "default_account", + "section_break_4", + "description", + "section_break_6", + "amount", + "cost_center", + "column_break_8", + "sanctioned_amount" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "expense_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Expense Date", - "length": 0, - "no_copy": 0, "oldfieldname": "expense_date", "oldfieldtype": "Date", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "150px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "150px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "expense_type", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Expense Claim Type", - "length": 0, - "no_copy": 0, "oldfieldname": "expense_type", "oldfieldtype": "Link", "options": "Expense Claim Type", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "150px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "150px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "depends_on": "expense_type", "fieldname": "default_account", "fieldtype": "Link", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Default Account", - "length": 0, - "no_copy": 0, "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_4", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "", "fieldname": "description", "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Description", - "length": 0, - "no_copy": 0, "oldfieldname": "description", "oldfieldtype": "Small Text", - "options": "", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "300px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "300px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_6", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "amount", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Amount", - "length": 0, - "no_copy": 0, "oldfieldname": "claim_amount", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "150px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "150px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_8", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "sanctioned_amount", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Sanctioned Amount", - "length": 0, "no_copy": 1, "oldfieldname": "sanctioned_amount", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "150px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "150px" + }, + { + "fieldname": "cost_center", + "fieldtype": "Link", + "label": "Cost Center", + "options": "Cost Center" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, "istable": 1, - "max_attachments": 0, - "modified": "2019-06-10 08:41:36.122565", + "links": [], + "modified": "2019-12-11 13:42:33.233432", "modified_by": "Administrator", "module": "HR", "name": "Expense Claim Detail", "owner": "harshada@webnotestech.com", "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/hr/doctype/expense_claim_type/expense_claim_type.js b/erpnext/hr/doctype/expense_claim_type/expense_claim_type.js index fda6809e6b2..d007e1a6c23 100644 --- a/erpnext/hr/doctype/expense_claim_type/expense_claim_type.js +++ b/erpnext/hr/doctype/expense_claim_type/expense_claim_type.js @@ -2,13 +2,13 @@ // License: GNU General Public License v3. See license.txt frappe.ui.form.on("Expense Claim Type", { - refresh: function(frm){ - frm.fields_dict["accounts"].grid.get_field("default_account").get_query = function(frm, cdt, cdn){ + refresh: function(frm) { + frm.fields_dict["accounts"].grid.get_field("default_account").get_query = function(doc, cdt, cdn) { var d = locals[cdt][cdn]; - return{ + return { filters: { "is_group": 0, - "root_type": "Expense", + "root_type": frm.doc.deferred_expense_account ? "Asset" : "Expense", 'company': d.company } } diff --git a/erpnext/hr/doctype/expense_claim_type/expense_claim_type.json b/erpnext/hr/doctype/expense_claim_type/expense_claim_type.json index d0c41221cc8..cf2cbb4308f 100644 --- a/erpnext/hr/doctype/expense_claim_type/expense_claim_type.json +++ b/erpnext/hr/doctype/expense_claim_type/expense_claim_type.json @@ -1,181 +1,74 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:expense_type", - "beta": 0, - "creation": "2012-03-27 14:35:55", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, - "engine": "InnoDB", + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:expense_type", + "creation": "2012-03-27 14:35:55", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "deferred_expense_account", + "expense_type", + "accounts", + "description" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "expense_type", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Expense Claim Type", - "length": 0, - "no_copy": 0, - "oldfieldname": "expense_type", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, + "fieldname": "expense_type", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Expense Claim Type", + "oldfieldname": "expense_type", + "oldfieldtype": "Data", + "reqd": 1, "unique": 1 - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "oldfieldname": "description", - "oldfieldtype": "Small Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "description", + "fieldtype": "Small Text", + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Small Text", "width": "300px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "accounts", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Accounts", - "length": 0, - "no_copy": 0, - "options": "Expense Claim Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "accounts", + "fieldtype": "Table", + "label": "Accounts", + "options": "Expense Claim Account" + }, + { + "default": "0", + "fieldname": "deferred_expense_account", + "fieldtype": "Check", + "label": "Deferred Expense Account" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-flag", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-09-18 14:13:43.770829", - "modified_by": "Administrator", - "module": "HR", - "name": "Expense Claim Type", - "owner": "harshada@webnotestech.com", + ], + "icon": "fa fa-flag", + "idx": 1, + "links": [], + "modified": "2019-12-11 13:38:59.534034", + "modified_by": "Administrator", + "module": "HR", + "name": "Expense Claim Type", + "owner": "harshada@webnotestech.com", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "Employee", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "read": 1, + "role": "Employee" } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_order": "ASC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "sort_field": "modified", + "sort_order": "ASC" } \ No newline at end of file diff --git a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json index 9bf69daf65a..96ad1b770d6 100644 --- a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json +++ b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "hash", "creation": "2019-06-03 11:42:33.123976", "doctype": "DocType", @@ -8,8 +9,8 @@ "field_order": [ "account_head", "cost_center", - "col_break1", "rate", + "col_break1", "description", "section_break_6", "tax_amount", @@ -93,7 +94,8 @@ } ], "istable": 1, - "modified": "2019-06-20 12:01:33.919555", + "links": [], + "modified": "2019-12-11 13:50:02.883328", "modified_by": "Administrator", "module": "HR", "name": "Expense Taxes and Charges", diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.js b/erpnext/hr/doctype/hr_settings/hr_settings.js index 4004c1cc2db..b629b42f9b1 100644 --- a/erpnext/hr/doctype/hr_settings/hr_settings.js +++ b/erpnext/hr/doctype/hr_settings/hr_settings.js @@ -2,10 +2,6 @@ // For license information, please see license.txt frappe.ui.form.on('HR Settings', { - refresh: function(frm) { - - }, - encrypt_salary_slips_in_emails: function(frm) { let encrypt_state = frm.doc.encrypt_salary_slips_in_emails; frm.set_df_property('password_policy', 'reqd', encrypt_state); @@ -19,5 +15,9 @@ frappe.ui.form.on('HR Settings', { } frm.set_value('password_policy', policy.split(new RegExp(" |-", 'g')).filter((token) => token).join('-')); } + }, + + restrict_backdated_leave_application: function(frm) { + frm.toggle_reqd("role_allowed_to_create_backdated_leave_application", frm.doc.restrict_backdated_leave_application); } -}); +}); \ No newline at end of file diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json index a41c887852e..90f49886f84 100644 --- a/erpnext/hr/doctype/hr_settings/hr_settings.json +++ b/erpnext/hr/doctype/hr_settings/hr_settings.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2013-08-02 13:45:23", "doctype": "DocType", "document_type": "Other", @@ -13,6 +14,7 @@ "expense_approver_mandatory_in_expense_claim", "payroll_settings", "include_holidays_in_total_working_days", + "disable_rounded_total", "max_working_hours_against_timesheet", "column_break_11", "email_salary_slip_to_employee", @@ -21,10 +23,12 @@ "leave_settings", "leave_approval_notification_template", "leave_status_notification_template", + "role_allowed_to_create_backdated_leave_application", "column_break_18", "leave_approver_mandatory_in_leave_application", "show_leaves_of_all_department_members_in_calendar", "auto_leave_encashment", + "restrict_backdated_leave_application", "hiring_settings", "check_vacancies" ], @@ -160,12 +164,33 @@ "fieldname": "auto_leave_encashment", "fieldtype": "Check", "label": "Auto Leave Encashment" + }, + { + "default": "0", + "description": "If checked, hides and disables Rounded Total field in Salary Slips", + "fieldname": "disable_rounded_total", + "fieldtype": "Check", + "label": "Disable Rounded Total" + }, + { + "default": "0", + "fieldname": "restrict_backdated_leave_application", + "fieldtype": "Check", + "label": "Restrict Backdated Leave Application" + }, + { + "depends_on": "eval:doc.restrict_backdated_leave_application == 1", + "fieldname": "role_allowed_to_create_backdated_leave_application", + "fieldtype": "Link", + "label": "Role Allowed to Create Backdated Leave Application", + "options": "Role" } ], "icon": "fa fa-cog", "idx": 1, "issingle": 1, - "modified": "2019-08-05 13:07:17.993968", + "links": [], + "modified": "2020-01-06 18:46:30.189815", "modified_by": "Administrator", "module": "HR", "name": "HR Settings", diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.py b/erpnext/hr/doctype/hr_settings/hr_settings.py index 2ee1b7bdda5..bf919067cae 100644 --- a/erpnext/hr/doctype/hr_settings/hr_settings.py +++ b/erpnext/hr/doctype/hr_settings/hr_settings.py @@ -7,6 +7,8 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document +from frappe.utils import cint +from frappe.custom.doctype.property_setter.property_setter import make_property_setter class HRSettings(Document): def validate(self): @@ -22,3 +24,12 @@ class HRSettings(Document): if self.email_salary_slip_to_employee and self.encrypt_salary_slips_in_emails: if not self.password_policy: frappe.throw(_("Password policy for Salary Slips is not set")) + + def on_update(self): + self.toggle_rounded_total() + frappe.clear_cache() + + def toggle_rounded_total(self): + self.disable_rounded_total = cint(self.disable_rounded_total) + make_property_setter("Salary Slip", "rounded_total", "hidden", self.disable_rounded_total, "Check") + make_property_setter("Salary Slip", "rounded_total", "print_hide", self.disable_rounded_total, "Check") diff --git a/erpnext/hr/doctype/job_offer/job_offer.js b/erpnext/hr/doctype/job_offer/job_offer.js index c3d83c48cca..558f4954d32 100755 --- a/erpnext/hr/doctype/job_offer/job_offer.js +++ b/erpnext/hr/doctype/job_offer/job_offer.js @@ -10,6 +10,10 @@ frappe.ui.form.on("Job Offer", { }); }, + setup: function (frm) { + frm.email_field = "applicant_email"; + }, + select_terms: function (frm) { erpnext.utils.get_terms(frm.doc.select_terms, frm.doc, function (r) { if (!r.exc) { diff --git a/erpnext/hr/doctype/job_offer/job_offer.json b/erpnext/hr/doctype/job_offer/job_offer.json index 7495c486bd2..ccbfdc53830 100644 --- a/erpnext/hr/doctype/job_offer/job_offer.json +++ b/erpnext/hr/doctype/job_offer/job_offer.json @@ -1,618 +1,191 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "HR-OFF-.YYYY.-.#####", - "beta": 0, - "creation": "2015-03-04 14:20:17.662207", - "custom": 0, - "default_print_format": "", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 0, + "actions": [], + "allow_import": 1, + "autoname": "HR-OFF-.YYYY.-.#####", + "creation": "2015-03-04 14:20:17.662207", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "job_applicant", + "applicant_name", + "applicant_email", + "column_break_3", + "status", + "offer_date", + "designation", + "company", + "section_break_4", + "offer_terms", + "section_break_14", + "select_terms", + "terms", + "printing_details", + "letter_head", + "column_break_16", + "select_print_heading", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "job_applicant", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Job Applicant", - "length": 0, - "no_copy": 0, - "options": "Job Applicant", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "job_applicant", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Job Applicant", + "options": "Job Applicant", + "print_hide": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "job_applicant.applicant_name", - "fieldname": "applicant_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Applicant Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "job_applicant.applicant_name", + "fieldname": "applicant_name", + "fieldtype": "Data", + "in_global_search": 1, + "in_list_view": 1, + "label": "Applicant Name", + "read_only": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "job_applicant.email_id", + "fieldname": "applicant_email", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Applicant Email Address", + "options": "Email", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Status", - "length": 0, - "no_copy": 1, - "options": "Awaiting Response\nAccepted\nRejected", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "offer_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Offer Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "fieldname": "status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Status", + "no_copy": 1, + "options": "Awaiting Response\nAccepted\nRejected", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "designation", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Designation", - "length": 0, - "no_copy": 0, - "options": "Designation", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "offer_date", + "fieldtype": "Date", + "label": "Offer Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "designation", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Designation", + "options": "Designation", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_4", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "print_hide": 1, + "remember_last_selected_value": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "offer_terms", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Job Offer Terms", - "length": 0, - "no_copy": 0, - "options": "Job Offer Term", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_4", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_14", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "offer_terms", + "fieldtype": "Table", + "label": "Job Offer Terms", + "options": "Job Offer Term" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "select_terms", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Select Terms and Conditions", - "length": 0, - "no_copy": 0, - "options": "Terms and Conditions", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_14", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "terms", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Terms and Conditions", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "select_terms", + "fieldtype": "Link", + "label": "Select Terms and Conditions", + "options": "Terms and Conditions", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "printing_details", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Printing Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "terms", + "fieldtype": "Text Editor", + "label": "Terms and Conditions" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "company.default_letter_head", - "fieldname": "letter_head", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Letter Head", - "length": 0, - "no_copy": 0, - "options": "Letter Head", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "printing_details", + "fieldtype": "Section Break", + "label": "Printing Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_16", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "allow_on_submit": 1, + "fetch_from": "company.default_letter_head", + "fieldname": "letter_head", + "fieldtype": "Link", + "label": "Letter Head", + "options": "Letter Head", + "print_hide": 1 + }, + { + "fieldname": "column_break_16", + "fieldtype": "Column Break", + "print_hide": 1, "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "select_print_heading", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Print Heading", - "length": 0, - "no_copy": 0, - "options": "Print Heading", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "fieldname": "select_print_heading", + "fieldtype": "Link", + "label": "Print Heading", + "options": "Print Heading", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Job Offer", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Job Offer", + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 16:15:35.616910", - "modified_by": "Administrator", - "module": "HR", - "name": "Job Offer", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2019-12-31 02:40:33.650728", + "modified_by": "Administrator", + "module": "HR", + "name": "Job Offer", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "applicant_name", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "applicant_name" } \ No newline at end of file diff --git a/erpnext/hr/doctype/job_offer/job_offer.py b/erpnext/hr/doctype/job_offer/job_offer.py index ef8004eedb7..32f1b89f49e 100644 --- a/erpnext/hr/doctype/job_offer/job_offer.py +++ b/erpnext/hr/doctype/job_offer/job_offer.py @@ -58,7 +58,8 @@ def get_staffing_plan_detail(designation, company, offer_date): @frappe.whitelist() def make_employee(source_name, target_doc=None): def set_missing_values(source, target): - target.personal_email = frappe.db.get_value("Job Applicant", source.job_applicant, "email_id") + target.personal_email, target.first_name = frappe.db.get_value("Job Applicant", \ + source.job_applicant, ["email_id", "applicant_name"]) doc = get_mapped_doc("Job Offer", source_name, { "Job Offer": { "doctype": "Employee", diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.py b/erpnext/hr/doctype/leave_allocation/leave_allocation.py index 874ae7a1bc2..d13bb4577cd 100755 --- a/erpnext/hr/doctype/leave_allocation/leave_allocation.py +++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.py @@ -69,10 +69,14 @@ class LeaveAllocation(Document): def validate_allocation_overlap(self): leave_allocation = frappe.db.sql(""" - select name from `tabLeave Allocation` - where employee=%s and leave_type=%s and docstatus=1 - and to_date >= %s and from_date <= %s""", - (self.employee, self.leave_type, self.from_date, self.to_date)) + SELECT + name + FROM `tabLeave Allocation` + WHERE + employee=%s AND leave_type=%s + AND name <> %s AND docstatus=1 + AND to_date >= %s AND from_date <= %s""", + (self.employee, self.leave_type, self.name, self.from_date, self.to_date)) if leave_allocation: frappe.msgprint(_("{0} already allocated for Employee {1} for period {2} to {3}") diff --git a/erpnext/hr/doctype/leave_application/leave_application.js b/erpnext/hr/doctype/leave_application/leave_application.js index 174641048b8..1f50e27098c 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.js +++ b/erpnext/hr/doctype/leave_application/leave_application.js @@ -60,7 +60,7 @@ frappe.ui.form.on("Leave Application", { } } }); - $("div").remove(".form-dashboard-section"); + $("div").remove(".form-dashboard-section.custom"); frm.dashboard.add_section( frappe.render_template('leave_application_dashboard', { data: leave_details @@ -104,11 +104,16 @@ frappe.ui.form.on("Leave Application", { }, half_day: function(frm) { - if (frm.doc.from_date == frm.doc.to_date) { - frm.set_value("half_day_date", frm.doc.from_date); + if (frm.doc.half_day) { + if (frm.doc.from_date == frm.doc.to_date) { + frm.set_value("half_day_date", frm.doc.from_date); + } + else { + frm.trigger("half_day_datepicker"); + } } else { - frm.trigger("half_day_datepicker"); + frm.set_value("half_day_date", ""); } frm.trigger("calculate_total_days"); }, @@ -171,7 +176,7 @@ frappe.ui.form.on("Leave Application", { frm.set_value('to_date', ''); return; } - // server call is done to include holidays in leave days calculations + // server call is done to include holidays in leave days calculations return frappe.call({ method: 'erpnext.hr.doctype.leave_application.leave_application.get_number_of_leave_days', args: { @@ -194,7 +199,7 @@ frappe.ui.form.on("Leave Application", { set_leave_approver: function(frm) { if(frm.doc.employee) { - // server call is done to include holidays in leave days calculations + // server call is done to include holidays in leave days calculations return frappe.call({ method: 'erpnext.hr.doctype.leave_application.leave_application.get_leave_approver', args: { diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py index e1e5e8001d8..96e4cb5bb30 100755 --- a/erpnext/hr/doctype/leave_application/leave_application.py +++ b/erpnext/hr/doctype/leave_application/leave_application.py @@ -54,12 +54,14 @@ class LeaveApplication(Document): self.create_leave_ledger_entry() self.reload() - def on_cancel(self): + def before_cancel(self): self.status = "Cancelled" + + def on_cancel(self): + self.create_leave_ledger_entry(submit=False) # notify leave applier about cancellation self.notify_employee() self.cancel_attendance() - self.create_leave_ledger_entry(submit=False) def validate_applicable_after(self): if self.leave_type: @@ -77,6 +79,12 @@ class LeaveApplication(Document): frappe.throw(_("{0} applicable after {1} working days").format(self.leave_type, leave_type.applicable_after)) def validate_dates(self): + if frappe.db.get_single_value("HR Settings", "restrict_backdated_leave_application"): + if self.from_date and self.from_date < frappe.utils.today(): + allowed_role = frappe.db.get_single_value("HR Settings", "role_allowed_to_create_backdated_leave_application") + if allowed_role not in frappe.get_roles(): + frappe.throw(_("Only users with the {0} role can create backdated leave applications").format(allowed_role)) + if self.from_date and self.to_date and (getdate(self.to_date) < getdate(self.from_date)): frappe.throw(_("To date cannot be before from date")) @@ -351,6 +359,9 @@ class LeaveApplication(Document): pass def create_leave_ledger_entry(self, submit=True): + if self.status != 'Approved' and submit: + return + expiry_date = get_allocation_expiry(self.employee, self.leave_type, self.to_date, self.from_date) @@ -517,8 +528,7 @@ def get_pending_leaves_for_period(employee, leave_type, from_date, to_date): def get_remaining_leaves(allocation, leaves_taken, date, expiry): ''' Returns minimum leaves remaining after comparing with remaining days for allocation expiry ''' - def _get_remaining_leaves(allocated_leaves, end_date): - remaining_leaves = flt(allocated_leaves) + flt(leaves_taken) + def _get_remaining_leaves(remaining_leaves, end_date): if remaining_leaves > 0: remaining_days = date_diff(end_date, date) + 1 @@ -526,10 +536,11 @@ def get_remaining_leaves(allocation, leaves_taken, date, expiry): return remaining_leaves - total_leaves = allocation.total_leaves_allocated + total_leaves = flt(allocation.total_leaves_allocated) + flt(leaves_taken) if expiry and allocation.unused_leaves: - remaining_leaves = _get_remaining_leaves(allocation.unused_leaves, expiry) + remaining_leaves = flt(allocation.unused_leaves) + flt(leaves_taken) + remaining_leaves = _get_remaining_leaves(remaining_leaves, expiry) total_leaves = flt(allocation.new_leaves_allocated) + flt(remaining_leaves) @@ -546,10 +557,10 @@ def get_leaves_for_period(employee, leave_type, from_date, to_date): leave_days += leave_entry.leaves elif inclusive_period and leave_entry.transaction_type == 'Leave Allocation' \ - and not skip_expiry_leaves(leave_entry, to_date): + and leave_entry.is_expired and not skip_expiry_leaves(leave_entry, to_date): leave_days += leave_entry.leaves - else: + elif leave_entry.transaction_type == 'Leave Application': if leave_entry.from_date < getdate(from_date): leave_entry.from_date = from_date if leave_entry.to_date > getdate(to_date): @@ -576,14 +587,15 @@ def skip_expiry_leaves(leave_entry, date): def get_leave_entries(employee, leave_type, from_date, to_date): ''' Returns leave entries between from_date and to_date ''' return frappe.db.sql(""" - select employee, leave_type, from_date, to_date, leaves, transaction_type, is_carry_forward, transaction_name - from `tabLeave Ledger Entry` - where employee=%(employee)s and leave_type=%(leave_type)s - and docstatus=1 - and leaves<0 - and (from_date between %(from_date)s and %(to_date)s - or to_date between %(from_date)s and %(to_date)s - or (from_date < %(from_date)s and to_date > %(to_date)s)) + SELECT + employee, leave_type, from_date, to_date, leaves, transaction_name, transaction_type, + is_carry_forward, is_expired + FROM `tabLeave Ledger Entry` + WHERE employee=%(employee)s AND leave_type=%(leave_type)s + AND docstatus=1 AND leaves<0 + AND (from_date between %(from_date)s AND %(to_date)s + OR to_date between %(from_date)s AND %(to_date)s + OR (from_date < %(from_date)s AND to_date > %(to_date)s)) """, { "from_date": from_date, "to_date": to_date, @@ -770,4 +782,4 @@ def get_leave_approver(employee): leave_approver = frappe.db.get_value('Department Approver', {'parent': department, 'parentfield': 'leave_approvers', 'idx': 1}, 'approver') - return leave_approver + return leave_approver \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_application/test_leave_application.py b/erpnext/hr/doctype/leave_application/test_leave_application.py index 38ae808f27e..b6216424de5 100644 --- a/erpnext/hr/doctype/leave_application/test_leave_application.py +++ b/erpnext/hr/doctype/leave_application/test_leave_application.py @@ -235,8 +235,8 @@ class TestLeaveApplication(unittest.TestCase): frappe.get_doc(dict( doctype = 'Holiday List', holiday_list_name = holiday_list, - from_date = date(date.today().year, 1, 1), - to_date = date(date.today().year, 12, 31), + from_date = add_months(today, -6), + to_date = add_months(today, 6), holidays = [ dict(holiday_date = today, description = 'Test') ] @@ -301,7 +301,7 @@ class TestLeaveApplication(unittest.TestCase): to_date = add_days(date, 2), company = "_Test Company", docstatus = 1, - status = "Approved" + status = "Approved" )) leave_application.submit() @@ -314,7 +314,7 @@ class TestLeaveApplication(unittest.TestCase): to_date = add_days(date, 8), company = "_Test Company", docstatus = 1, - status = "Approved" + status = "Approved" )) self.assertRaises(frappe.ValidationError, leave_application.insert) @@ -597,8 +597,8 @@ def get_leave_period(): return frappe.get_doc(dict( name = 'Test Leave Period', doctype = 'Leave Period', - from_date = "{0}-12-01".format(now_datetime().year - 1), - to_date = "{0}-12-31".format(now_datetime().year), + from_date = add_months(nowdate(), -6), + to_date = add_months(nowdate(), 6), company = "_Test Company", is_active = 1 )).insert() diff --git a/erpnext/hr/doctype/leave_control_panel/leave_control_panel.json b/erpnext/hr/doctype/leave_control_panel/leave_control_panel.json index 39985fa2ef2..d9c592aa64e 100644 --- a/erpnext/hr/doctype/leave_control_panel/leave_control_panel.json +++ b/erpnext/hr/doctype/leave_control_panel/leave_control_panel.json @@ -1,25 +1,28 @@ { + "actions": [], "allow_copy": 1, "creation": "2013-01-10 16:34:15", "doctype": "DocType", + "engine": "InnoDB", "field_order": [ "select_employees_section", "company", "employment_type", "branch", - "column_break1", "department", + "column_break1", "designation", "employee_grade", "employee", "allocate_leaves_section", "from_date", "to_date", - "leave_policy", - "leave_type", "carry_forward", "no_of_days", - "allocate" + "allocate", + "column_break_16", + "leave_policy", + "leave_type" ], "fields": [ { @@ -125,13 +128,18 @@ "fieldname": "allocate_leaves_section", "fieldtype": "Section Break", "label": "Allocate Leaves" + }, + { + "fieldname": "column_break_16", + "fieldtype": "Column Break" } ], "hide_toolbar": 1, "icon": "fa fa-cog", "idx": 1, "issingle": 1, - "modified": "2019-05-24 09:41:34.105741", + "links": [], + "modified": "2019-12-12 18:51:41.573349", "modified_by": "Administrator", "module": "HR", "name": "Leave Control Panel", diff --git a/erpnext/hr/doctype/leave_encashment/leave_encashment.json b/erpnext/hr/doctype/leave_encashment/leave_encashment.json index 29f634e8649..2cf6ccf5ca0 100644 --- a/erpnext/hr/doctype/leave_encashment/leave_encashment.json +++ b/erpnext/hr/doctype/leave_encashment/leave_encashment.json @@ -1,577 +1,198 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "HR-ENC-.YYYY.-.#####", - "beta": 0, - "creation": "2018-04-13 15:31:51.197046", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "HR-ENC-.YYYY.-.#####", + "creation": "2018-04-13 15:31:51.197046", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "leave_period", + "employee", + "employee_name", + "department", + "column_break_4", + "leave_type", + "leave_allocation", + "leave_balance", + "encashable_days", + "amended_from", + "payroll", + "encashment_amount", + "encashment_date", + "additional_salary" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "leave_period", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Leave Period", - "length": 0, - "no_copy": 0, - "options": "Leave Period", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "leave_period", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Leave Period", + "options": "Leave Period", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Employee Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "label": "Department", + "options": "Department", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "leave_type", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Leave Type", - "length": 0, - "no_copy": 0, - "options": "Leave Type", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "leave_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Leave Type", + "options": "Leave Type", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "leave_allocation", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Leave Allocation", - "length": 0, - "no_copy": 1, - "options": "Leave Allocation", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "leave_allocation", + "fieldtype": "Link", + "label": "Leave Allocation", + "no_copy": 1, + "options": "Leave Allocation", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "leave_balance", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Leave Balance", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "leave_balance", + "fieldtype": "Float", + "label": "Leave Balance", + "no_copy": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "encashable_days", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Encashable days", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "encashable_days", + "fieldtype": "Float", + "label": "Encashable days", + "no_copy": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Leave Encashment", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Leave Encashment", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "payroll", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payroll", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "payroll", + "fieldtype": "Section Break", + "label": "Payroll" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "encashment_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Encashment Amount", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "encashment_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Encashment Amount", + "no_copy": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "encashment_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Encashment Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Today", + "fieldname": "encashment_date", + "fieldtype": "Date", + "label": "Encashment Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "additional_salary", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Additional Salary", - "length": 0, - "no_copy": 1, - "options": "Additional Salary", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "additional_salary", + "fieldtype": "Link", + "label": "Additional Salary", + "no_copy": 1, + "options": "Additional Salary", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 16:15:43.829272", - "modified_by": "Administrator", - "module": "HR", - "name": "Leave Encashment", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2019-12-16 11:51:57.732223", + "modified_by": "Administrator", + "module": "HR", + "name": "Leave Encashment", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Employee", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Employee", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_period/test_leave_period.py b/erpnext/hr/doctype/leave_period/test_leave_period.py index 850a08dd536..1762cf917a2 100644 --- a/erpnext/hr/doctype/leave_period/test_leave_period.py +++ b/erpnext/hr/doctype/leave_period/test_leave_period.py @@ -43,10 +43,18 @@ class TestLeavePeriod(unittest.TestCase): leave_period.grant_leave_allocation(employee=employee_doc_name) self.assertEqual(get_leave_balance_on(employee_doc_name, leave_type, today()), 20) -def create_leave_period(from_date, to_date): +def create_leave_period(from_date, to_date, company=None): + leave_period = frappe.db.get_value('Leave Period', + dict(company=company or erpnext.get_default_company(), + from_date=from_date, + to_date=to_date, + is_active=1), 'name') + if leave_period: + return frappe.get_doc("Leave Period", leave_period) + leave_period = frappe.get_doc({ "doctype": "Leave Period", - "company": erpnext.get_default_company(), + "company": company or erpnext.get_default_company(), "from_date": from_date, "to_date": to_date, "is_active": 1 diff --git a/erpnext/hr/doctype/leave_type/leave_type.json b/erpnext/hr/doctype/leave_type/leave_type.json index 550d536c8df..0af832f903e 100644 --- a/erpnext/hr/doctype/leave_type/leave_type.json +++ b/erpnext/hr/doctype/leave_type/leave_type.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:leave_type_name", @@ -24,10 +25,12 @@ "encashment", "allow_encashment", "encashment_threshold_days", + "column_break_17", "earning_component", "earned_leave", "is_earned_leave", "earned_leave_frequency", + "column_break_22", "rounding" ], "fields": [ @@ -172,11 +175,20 @@ "fieldname": "maximum_carry_forwarded_leaves", "fieldtype": "Float", "label": "Maximum Carry Forwarded Leaves" + }, + { + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_22", + "fieldtype": "Column Break" } ], "icon": "fa fa-flag", "idx": 1, - "modified": "2019-09-06 18:48:48.946074", + "links": [], + "modified": "2019-12-12 12:48:37.780254", "modified_by": "Administrator", "module": "HR", "name": "Leave Type", diff --git a/erpnext/hr/doctype/loan/loan.json b/erpnext/hr/doctype/loan/loan.json index 505b601edd5..2b2d4818277 100644 --- a/erpnext/hr/doctype/loan/loan.json +++ b/erpnext/hr/doctype/loan/loan.json @@ -1,1200 +1,298 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "ACC-LOAN-.YYYY.-.#####", - "beta": 0, - "creation": "2016-12-02 10:11:49.673604", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_import": 1, + "autoname": "ACC-LOAN-.YYYY.-.#####", + "creation": "2016-12-02 10:11:49.673604", + "doctype": "DocType", + "document_type": "Document", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "applicant_type", + "applicant", + "applicant_name", + "loan_type", + "loan_application", + "column_break_3", + "posting_date", + "company", + "status", + "repay_from_salary", + "section_break_8", + "loan_amount", + "rate_of_interest", + "disbursement_date", + "repayment_start_date", + "column_break_11", + "repayment_method", + "repayment_periods", + "monthly_repayment_amount", + "account_info", + "mode_of_payment", + "payment_account", + "column_break_9", + "loan_account", + "interest_income_account", + "section_break_15", + "repayment_schedule", + "section_break_17", + "total_payment", + "column_break_19", + "total_interest_payable", + "total_amount_paid", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "applicant_type", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Applicant Type", - "length": 0, - "no_copy": 0, - "options": "Employee\nMember", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "applicant_type", + "fieldtype": "Select", + "label": "Applicant Type", + "options": "Employee\nMember", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "applicant", - "fieldtype": "Dynamic Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Applicant", - "length": 0, - "no_copy": 0, - "options": "applicant_type", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "applicant", + "fieldtype": "Dynamic Link", + "in_standard_filter": 1, + "label": "Applicant", + "options": "applicant_type", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "applicant_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Applicant Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "applicant_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Applicant Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "loan_application", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Loan Application", - "length": 0, - "no_copy": 0, - "options": "Loan Application", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "loan_application", + "fieldtype": "Link", + "label": "Loan Application", + "options": "Loan Application" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "loan_type", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Loan Type", - "length": 0, - "no_copy": 0, - "options": "Loan Type", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "loan_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Loan Type", + "options": "Loan Type", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fetch_if_empty": 0, - "fieldname": "posting_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Posting Date", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Posting Date", + "no_copy": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "remember_last_selected_value": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Sanctioned", - "fetch_if_empty": 0, - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 1, - "options": "Sanctioned\nDisbursed\nRepaid/Closed", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Sanctioned", + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "no_copy": 1, + "options": "Sanctioned\nDisbursed\nRepaid/Closed", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.applicant_type==\"Employee\"", - "fetch_if_empty": 0, - "fieldname": "repay_from_salary", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Repay from Salary", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "depends_on": "eval:doc.applicant_type==\"Employee\"", + "fieldname": "repay_from_salary", + "fieldtype": "Check", + "label": "Repay from Salary" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_8", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Loan Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_8", + "fieldtype": "Section Break", + "label": "Loan Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "loan_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Loan Amount", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "loan_amount", + "fieldtype": "Currency", + "label": "Loan Amount", + "options": "Company:company:default_currency", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fetch_from": "loan_type.rate_of_interest", - "fetch_if_empty": 0, - "fieldname": "rate_of_interest", - "fieldtype": "Percent", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rate of Interest (%) / Year", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "loan_type.rate_of_interest", + "fieldname": "rate_of_interest", + "fieldtype": "Percent", + "label": "Rate of Interest (%) / Year", + "read_only": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.status==\"Disbursed\"", - "fetch_if_empty": 0, - "fieldname": "disbursement_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Disbursement Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.status==\"Disbursed\"", + "fieldname": "disbursement_date", + "fieldtype": "Date", + "label": "Disbursement Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "repayment_start_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Repayment Start Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "repayment_start_date", + "fieldtype": "Date", + "label": "Repayment Start Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_11", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Repay Over Number of Periods", - "fetch_if_empty": 0, - "fieldname": "repayment_method", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Repayment Method", - "length": 0, - "no_copy": 0, - "options": "\nRepay Fixed Amount per Period\nRepay Over Number of Periods", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Repay Over Number of Periods", + "fieldname": "repayment_method", + "fieldtype": "Select", + "label": "Repayment Method", + "options": "\nRepay Fixed Amount per Period\nRepay Over Number of Periods", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "depends_on": "", - "fetch_if_empty": 0, - "fieldname": "repayment_periods", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Repayment Period in Months", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "repayment_periods", + "fieldtype": "Int", + "label": "Repayment Period in Months" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "depends_on": "", - "fetch_if_empty": 0, - "fieldname": "monthly_repayment_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Monthly Repayment Amount", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "monthly_repayment_amount", + "fieldtype": "Currency", + "label": "Monthly Repayment Amount", + "options": "Company:company:default_currency" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "account_info", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Account Info", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "account_info", + "fieldtype": "Section Break", + "label": "Account Info" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "mode_of_payment", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Mode of Payment", - "length": 0, - "no_copy": 0, - "options": "Mode of Payment", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "mode_of_payment", + "fieldtype": "Link", + "label": "Mode of Payment", + "options": "Mode of Payment", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "payment_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payment Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "payment_account", + "fieldtype": "Link", + "label": "Payment Account", + "options": "Account", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_9", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "loan_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Loan Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "loan_account", + "fieldtype": "Link", + "label": "Loan Account", + "options": "Account", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "interest_income_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Interest Income Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "interest_income_account", + "fieldtype": "Link", + "label": "Interest Income Account", + "options": "Account", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_15", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Repayment Schedule", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_15", + "fieldtype": "Section Break", + "label": "Repayment Schedule" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "repayment_schedule", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Repayment Schedule", - "length": 0, - "no_copy": 1, - "options": "Repayment Schedule", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "repayment_schedule", + "fieldtype": "Table", + "label": "Repayment Schedule", + "no_copy": 1, + "options": "Repayment Schedule" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_17", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Totals", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_17", + "fieldtype": "Section Break", + "label": "Totals" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fetch_if_empty": 0, - "fieldname": "total_payment", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Payment", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "total_payment", + "fieldtype": "Currency", + "label": "Total Payment", + "options": "Company:company:default_currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_19", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_19", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fetch_if_empty": 0, - "fieldname": "total_interest_payable", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Interest Payable", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "total_interest_payable", + "fieldtype": "Currency", + "label": "Total Interest Payable", + "options": "Company:company:default_currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "total_amount_paid", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Amount Paid", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "fieldname": "total_amount_paid", + "fieldtype": "Currency", + "label": "Total Amount Paid", + "options": "Company:company:default_currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Loan", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Loan", + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-07-10 13:04:20.953694", - "modified_by": "Administrator", - "module": "HR", - "name": "Loan", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2019-12-12 14:45:38.823072", + "modified_by": "Administrator", + "module": "HR", + "name": "Loan", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "Employee", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 + "read": 1, + "role": "Employee" } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "search_fields": "posting_date", - "show_name_in_global_search": 0, - "sort_field": "creation", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "search_fields": "posting_date", + "sort_field": "creation", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.js b/erpnext/hr/doctype/payroll_entry/payroll_entry.js index adc06712ef0..d25eb6d7810 100644 --- a/erpnext/hr/doctype/payroll_entry/payroll_entry.js +++ b/erpnext/hr/doctype/payroll_entry/payroll_entry.js @@ -31,7 +31,11 @@ frappe.ui.form.on('Payroll Entry', { } if ((frm.doc.employees || []).length) { frm.page.set_primary_action(__('Create Salary Slips'), () => { - frm.save('Submit'); + frm.save('Submit').then(()=>{ + frm.page.clear_primary_action(); + frm.refresh(); + frm.events.refresh(frm); + }); }); } } diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.py b/erpnext/hr/doctype/payroll_entry/payroll_entry.py index 2de01e6394d..dfd38ebbe38 100644 --- a/erpnext/hr/doctype/payroll_entry/payroll_entry.py +++ b/erpnext/hr/doctype/payroll_entry/payroll_entry.py @@ -163,7 +163,7 @@ class PayrollEntry(Document): """ cond = self.get_filter_condition() return frappe.db.sql(""" select eld.loan_account, eld.loan, - eld.interest_income_account, eld.principal_amount, eld.interest_amount, eld.total_payment + eld.interest_income_account, eld.principal_amount, eld.interest_amount, eld.total_payment,t1.employee from `tabSalary Slip` t1, `tabSalary Slip Loan` eld where @@ -246,6 +246,7 @@ class PayrollEntry(Document): accounts.append({ "account": acc, "debit_in_account_currency": flt(amount, precision), + "party_type": '', "cost_center": self.cost_center, "project": self.project }) @@ -257,6 +258,7 @@ class PayrollEntry(Document): "account": acc, "credit_in_account_currency": flt(amount, precision), "cost_center": self.cost_center, + "party_type": '', "project": self.project }) @@ -264,7 +266,9 @@ class PayrollEntry(Document): for data in loan_details: accounts.append({ "account": data.loan_account, - "credit_in_account_currency": data.principal_amount + "credit_in_account_currency": data.principal_amount, + "party_type": "Employee", + "party": data.employee }) if data.interest_amount and not data.interest_income_account: @@ -275,14 +279,17 @@ class PayrollEntry(Document): "account": data.interest_income_account, "credit_in_account_currency": data.interest_amount, "cost_center": self.cost_center, - "project": self.project + "project": self.project, + "party_type": "Employee", + "party": data.employee }) payable_amount -= flt(data.total_payment, precision) # Payable amount accounts.append({ "account": default_payroll_payable_account, - "credit_in_account_currency": flt(payable_amount, precision) + "credit_in_account_currency": flt(payable_amount, precision), + "party_type": '', }) journal_entry.set("accounts", accounts) @@ -546,7 +553,6 @@ def submit_salary_slips_for_employees(payroll_entry, salary_slips, publish_progr count += 1 if publish_progress: frappe.publish_progress(count*100/len(salary_slips), title = _("Submitting Salary Slips...")) - if submitted_ss: payroll_entry.make_accrual_jv_entry() frappe.msgprint(_("Salary Slip submitted for period from {0} to {1}") diff --git a/erpnext/hr/doctype/repayment_schedule/repayment_schedule.json b/erpnext/hr/doctype/repayment_schedule/repayment_schedule.json index a1161851d04..5bb2d370fa8 100644 --- a/erpnext/hr/doctype/repayment_schedule/repayment_schedule.json +++ b/erpnext/hr/doctype/repayment_schedule/repayment_schedule.json @@ -1,231 +1,82 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-12-20 15:32:25.078334", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "creation": "2016-12-20 15:32:25.078334", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "payment_date", + "principal_amount", + "interest_amount", + "total_payment", + "balance_loan_amount", + "paid" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "payment_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Payment Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "columns": 2, + "fieldname": "payment_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Payment Date" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "principal_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Principal Amount", - "length": 0, - "no_copy": 1, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "principal_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Principal Amount", + "no_copy": 1, + "options": "Company:company:default_currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "interest_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Interest Amount", - "length": 0, - "no_copy": 1, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "interest_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Interest Amount", + "no_copy": 1, + "options": "Company:company:default_currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "total_payment", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Total Payment", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "total_payment", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Total Payment", + "options": "Company:company:default_currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "balance_loan_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Balance Loan Amount", - "length": 0, - "no_copy": 1, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "balance_loan_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Balance Loan Amount", + "no_copy": 1, + "options": "Company:company:default_currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "paid", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Paid", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "0", + "fieldname": "paid", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Paid", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-03-30 17:37:31.834792", - "modified_by": "Administrator", - "module": "HR", - "name": "Repayment Schedule", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "istable": 1, + "modified": "2019-10-29 11:45:10.694557", + "modified_by": "Administrator", + "module": "HR", + "name": "Repayment Schedule", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_detail/salary_detail.json b/erpnext/hr/doctype/salary_detail/salary_detail.json index edf2786821e..bde735d3bc9 100644 --- a/erpnext/hr/doctype/salary_detail/salary_detail.json +++ b/erpnext/hr/doctype/salary_detail/salary_detail.json @@ -1,765 +1,203 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-06-30 15:32:36.385111", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, + "actions": [], + "creation": "2016-06-30 15:32:36.385111", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "salary_component", + "abbr", + "statistical_component", + "column_break_3", + "deduct_full_tax_on_selected_payroll_date", + "depends_on_payment_days", + "is_tax_applicable", + "is_flexible_benefit", + "variable_based_on_taxable_salary", + "section_break_2", + "condition", + "amount_based_on_formula", + "formula", + "amount", + "do_not_include_in_total", + "default_amount", + "additional_amount", + "tax_on_flexible_benefit", + "tax_on_additional_salary", + "section_break_11", + "condition_and_formula_help" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "salary_component", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Component", - "length": 0, - "no_copy": 0, - "options": "Salary Component", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "salary_component", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Component", + "options": "Salary Component", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 1, - "default": "", - "depends_on": "eval:doc.parenttype=='Salary Structure'", - "fetch_from": "salary_component.salary_component_abbr", - "fetch_if_empty": 0, - "fieldname": "abbr", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Abbr", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "columns": 1, + "depends_on": "eval:doc.parenttype=='Salary Structure'", + "fetch_from": "salary_component.salary_component_abbr", + "fieldname": "abbr", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Abbr", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ", - "fetch_from": "salary_component.statistical_component", - "fetch_if_empty": 0, - "fieldname": "statistical_component", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Statistical Component", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "description": "If selected, the value specified or calculated in this component will not contribute to the earnings or deductions. However, it's value can be referenced by other components that can be added or deducted. ", + "fetch_from": "salary_component.statistical_component", + "fieldname": "statistical_component", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Statistical Component" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "salary_component.is_tax_applicable", - "fetch_if_empty": 0, - "fieldname": "is_tax_applicable", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Tax Applicable", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fetch_from": "salary_component.is_tax_applicable", + "fieldname": "is_tax_applicable", + "fieldtype": "Check", + "label": "Is Tax Applicable", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "salary_component.is_flexible_benefit", - "fetch_if_empty": 0, - "fieldname": "is_flexible_benefit", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Flexible Benefit", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fetch_from": "salary_component.is_flexible_benefit", + "fieldname": "is_flexible_benefit", + "fieldtype": "Check", + "label": "Is Flexible Benefit", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fetch_from": "salary_component.variable_based_on_taxable_salary", - "fetch_if_empty": 0, - "fieldname": "variable_based_on_taxable_salary", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Variable Based On Taxable Salary", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fetch_from": "salary_component.variable_based_on_taxable_salary", + "fieldname": "variable_based_on_taxable_salary", + "fieldtype": "Check", + "label": "Variable Based On Taxable Salary", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_from": "salary_component.depends_on_payment_days", - "fetch_if_empty": 0, - "fieldname": "depends_on_payment_days", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Depends on Payment Days", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fetch_from": "salary_component.depends_on_payment_days", + "fieldname": "depends_on_payment_days", + "fieldtype": "Check", + "label": "Depends on Payment Days", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "deduct_full_tax_on_selected_payroll_date", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Deduct Full Tax on Selected Payroll Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "deduct_full_tax_on_selected_payroll_date", + "fieldtype": "Check", + "label": "Deduct Full Tax on Selected Payroll Date", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.is_flexible_benefit != 1", - "fetch_if_empty": 0, - "fieldname": "section_break_2", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.is_flexible_benefit != 1", + "fieldname": "section_break_2", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.parenttype=='Salary Structure'", - "fetch_if_empty": 0, - "fieldname": "condition", - "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Condition", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "depends_on": "eval:doc.parenttype=='Salary Structure'", + "fieldname": "condition", + "fieldtype": "Code", + "label": "Condition" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "depends_on": "eval:doc.parenttype=='Salary Structure'", - "fetch_from": "", - "fetch_if_empty": 0, - "fieldname": "amount_based_on_formula", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amount based on formula", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "depends_on": "eval:doc.parenttype=='Salary Structure'", + "fieldname": "amount_based_on_formula", + "fieldtype": "Check", + "label": "Amount based on formula" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "depends_on": "eval:doc.amount_based_on_formula!==0 && doc.parenttype==='Salary Structure'", - "description": "", - "fetch_if_empty": 0, - "fieldname": "formula", - "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Formula", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "depends_on": "eval:doc.amount_based_on_formula!==0 && doc.parenttype==='Salary Structure'", + "fieldname": "formula", + "fieldtype": "Code", + "in_list_view": 1, + "label": "Formula" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.amount_based_on_formula!==1 || doc.parenttype==='Salary Slip'", - "fetch_if_empty": 0, - "fieldname": "amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Amount", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.amount_based_on_formula!==1 || doc.parenttype==='Salary Slip'", + "fieldname": "amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Amount", + "options": "Company:company:default_currency" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "do_not_include_in_total", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Do not include in total", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "do_not_include_in_total", + "fieldtype": "Check", + "label": "Do not include in total" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.parenttype=='Salary Structure'", - "fetch_if_empty": 0, - "fieldname": "default_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Default Amount", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.parenttype=='Salary Structure'", + "fieldname": "default_amount", + "fieldtype": "Currency", + "label": "Default Amount", + "options": "Company:company:default_currency", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fetch_from": "", - "fetch_if_empty": 0, - "fieldname": "additional_amount", - "fieldtype": "Currency", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Additional Amount", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "additional_amount", + "fieldtype": "Currency", + "hidden": 1, + "label": "Additional Amount", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='deductions' && doc.variable_based_on_taxable_salary == 1", - "fetch_if_empty": 0, - "fieldname": "tax_on_flexible_benefit", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Tax on flexible benefit", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='deductions' && doc.variable_based_on_taxable_salary == 1", + "fieldname": "tax_on_flexible_benefit", + "fieldtype": "Currency", + "label": "Tax on flexible benefit", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='deductions' && doc.variable_based_on_taxable_salary == 1", - "fetch_if_empty": 0, - "fieldname": "tax_on_additional_salary", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Tax on additional salary", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.parenttype=='Salary Slip' && doc.parentfield=='deductions' && doc.variable_based_on_taxable_salary == 1", + "fieldname": "tax_on_additional_salary", + "fieldtype": "Currency", + "label": "Tax on additional salary", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.parenttype=='Salary Structure'", - "fetch_if_empty": 0, - "fieldname": "section_break_11", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:doc.parenttype=='Salary Structure'", + "fieldname": "section_break_11", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.parenttype=='Salary Structure'", - "fetch_if_empty": 0, - "fieldname": "condition_and_formula_help", - "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Condition and Formula Help", - "length": 0, - "no_copy": 0, - "options": "

Condition and Formula Help

\n\n

Notes:

\n\n
    \n
  1. Use field base for using base salary of the Employee
  2. \n
  3. Use Salary Component abbreviations in conditions and formulas. BS = Basic Salary
  4. \n
  5. Use field name for employee details in conditions and formulas. Employment Type = employment_typeBranch = branch
  6. \n
  7. Use field name from Salary Slip in conditions and formulas. Payment Days = payment_daysLeave without pay = leave_without_pay
  8. \n
  9. Direct Amount can also be entered based on Condtion. See example 3
\n\n

Examples

\n
    \n
  1. Calculating Basic Salary based on base\n
    Condition: base < 10000
    \n
    Formula: base * .2
  2. \n
  3. Calculating HRA based on Basic SalaryBS \n
    Condition: BS > 2000
    \n
    Formula: BS * .1
  4. \n
  5. Calculating TDS based on Employment Typeemployment_type \n
    Condition: employment_type==\"Intern\"
    \n
    Amount: 1000
  6. \n
", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "depends_on": "eval:doc.parenttype=='Salary Structure'", + "fieldname": "condition_and_formula_help", + "fieldtype": "HTML", + "label": "Condition and Formula Help", + "options": "

Condition and Formula Help

\n\n

Notes:

\n\n
    \n
  1. Use field base for using base salary of the Employee
  2. \n
  3. Use Salary Component abbreviations in conditions and formulas. BS = Basic Salary
  4. \n
  5. Use field name for employee details in conditions and formulas. Employment Type = employment_typeBranch = branch
  6. \n
  7. Use field name from Salary Slip in conditions and formulas. Payment Days = payment_daysLeave without pay = leave_without_pay
  8. \n
  9. Direct Amount can also be entered based on Condtion. See example 3
\n\n

Examples

\n
    \n
  1. Calculating Basic Salary based on base\n
    Condition: base < 10000
    \n
    Formula: base * .2
  2. \n
  3. Calculating HRA based on Basic SalaryBS \n
    Condition: BS > 2000
    \n
    Formula: BS * .1
  4. \n
  5. Calculating TDS based on Employment Typeemployment_type \n
    Condition: employment_type==\"Intern\"
    \n
    Amount: 1000
  6. \n
" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-05-11 17:33:08.508653", - "modified_by": "Administrator", - "module": "HR", - "name": "Salary Detail", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "istable": 1, + "links": [], + "modified": "2019-12-31 17:15:25.646689", + "modified_by": "Administrator", + "module": "HR", + "name": "Salary Detail", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.json b/erpnext/hr/doctype/salary_slip/salary_slip.json index fa3114cdaac..e73d41a28ed 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.json +++ b/erpnext/hr/doctype/salary_slip/salary_slip.json @@ -1,1955 +1,508 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "beta": 0, - "creation": "2013-01-10 16:34:15", - "custom": 0, - "default_print_format": "", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, - "fields": [ - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fieldname": "posting_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Posting Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Employee", - "length": 0, - "no_copy": 0, - "oldfieldname": "employee", - "oldfieldtype": "Link", - "options": "Employee", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Employee Name", - "length": 0, - "no_copy": 0, - "oldfieldname": "employee_name", - "oldfieldtype": "Data", - "options": "", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Department", - "length": 0, - "no_copy": 0, - "oldfieldname": "department", - "oldfieldtype": "Link", - "options": "Department", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.designation", - "fetch_from": "employee.designation", - "fieldname": "designation", - "fieldtype": "Read Only", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Designation", - "length": 0, - "no_copy": 0, - "oldfieldname": "designation", - "oldfieldtype": "Link", - "options": "", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.branch", - "fieldname": "branch", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Branch", - "length": 0, - "no_copy": 0, - "oldfieldname": "branch", - "oldfieldtype": "Link", - "options": "Branch", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break1", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, - "width": "50%" - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 0, - "options": "Draft\nSubmitted\nCancelled", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "journal_entry", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Journal Entry", - "length": 0, - "no_copy": 0, - "options": "Journal Entry", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "payroll_entry", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payroll Entry", - "length": 0, - "no_copy": 0, - "options": "Payroll Entry", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "letter_head", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Letter Head", - "length": 0, - "no_copy": 0, - "options": "Letter Head", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_10", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "salary_slip_based_on_timesheet", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Salary Slip Based on Timesheet", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "start_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Start Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "depends_on": "", - "fieldname": "end_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "End Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_15", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "salary_structure", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Salary Structure", - "length": 0, - "no_copy": 0, - "options": "Salary Structure", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "depends_on": "eval:(!doc.salary_slip_based_on_timesheet)", - "fieldname": "payroll_frequency", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payroll Frequency", - "length": 0, - "no_copy": 0, - "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "total_working_days", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Working Days", - "length": 0, - "no_copy": 0, - "oldfieldname": "total_days_in_month", - "oldfieldtype": "Int", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "leave_without_pay", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Leave Without Pay", - "length": 0, - "no_copy": 0, - "oldfieldname": "leave_without_pay", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "payment_days", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payment Days", - "length": 0, - "no_copy": 0, - "oldfieldname": "payment_days", - "oldfieldtype": "Float", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "hourly_wages", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "timesheets", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Salary Slip Timesheet", - "length": 0, - "no_copy": 0, - "options": "Salary Slip Timesheet", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_20", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_working_hours", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Working Hours", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 1, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "hour_rate", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Hour Rate", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 1, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "section_break_26", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bank Name", - "length": 0, - "no_copy": 0, - "oldfieldname": "bank_name", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bank_account_no", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Bank Account No.", - "length": 0, - "no_copy": 0, - "oldfieldname": "bank_account_no", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_32", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "deduct_tax_for_unclaimed_employee_benefits", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Deduct Tax For Unclaimed Employee Benefits", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "deduct_tax_for_unsubmitted_tax_exemption_proof", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Deduct Tax For Unsubmitted Tax Exemption Proof", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "earning_deduction", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Earning & Deduction", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "earning", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Earning", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, - "width": "50%" - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "earnings", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Earnings", - "length": 0, - "no_copy": 0, - "oldfieldname": "earning_details", - "oldfieldtype": "Table", - "options": "Salary Detail", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "deduction", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Deduction", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, - "width": "50%" - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "deductions", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Deductions", - "length": 0, - "no_copy": 0, - "oldfieldname": "deduction_details", - "oldfieldtype": "Table", - "options": "Salary Detail", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "totals", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "gross_pay", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Gross Pay", - "length": 0, - "no_copy": 0, - "oldfieldname": "gross_pay", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_25", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_deduction", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Deduction", - "length": 0, - "no_copy": 0, - "oldfieldname": "total_deduction", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "total_loan_repayment", - "fieldname": "loan_repayment", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Loan repayment", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "loans", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Employee Loan", - "length": 0, - "no_copy": 0, - "options": "Salary Slip Loan", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_43", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "total_principal_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Principal Amount", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "total_interest_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Interest Amount", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_45", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fieldname": "total_loan_repayment", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Loan Repayment", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "net_pay_info", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "net pay info", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Gross Pay - Total Deduction - Loan Repayment", - "fieldname": "net_pay", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Net Pay", - "length": 0, - "no_copy": 0, - "oldfieldname": "net_pay", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_53", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "fieldname": "rounded_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rounded Total", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_55", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Net Pay (in words) will be visible once you save the Salary Slip.", - "fieldname": "total_in_words", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total in words", - "length": 0, - "no_copy": 0, - "oldfieldname": "net_pay_in_words", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "oldfieldname": "amended_from", - "oldfieldtype": "Data", - "options": "Salary Slip", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-file-text", - "idx": 9, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-06-19 13:10:14.524119", - "modified_by": "Administrator", - "module": "HR", - "name": "Salary Slip", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "Employee", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "timeline_field": "employee", - "title_field": "employee_name", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 - } \ No newline at end of file + "actions": [], + "allow_import": 1, + "creation": "2013-01-10 16:34:15", + "doctype": "DocType", + "document_type": "Setup", + "engine": "InnoDB", + "field_order": [ + "posting_date", + "employee", + "employee_name", + "department", + "designation", + "column_break1", + "company", + "journal_entry", + "payroll_entry", + "letter_head", + "branch", + "status", + "section_break_10", + "salary_slip_based_on_timesheet", + "payroll_frequency", + "start_date", + "end_date", + "column_break_15", + "salary_structure", + "total_working_days", + "leave_without_pay", + "payment_days", + "hourly_wages", + "timesheets", + "column_break_20", + "total_working_hours", + "hour_rate", + "section_break_26", + "bank_name", + "bank_account_no", + "section_break_32", + "deduct_tax_for_unclaimed_employee_benefits", + "deduct_tax_for_unsubmitted_tax_exemption_proof", + "earning_deduction", + "earning", + "earnings", + "deduction", + "deductions", + "totals", + "gross_pay", + "column_break_25", + "total_deduction", + "loan_repayment", + "loans", + "section_break_43", + "total_principal_amount", + "total_interest_amount", + "column_break_45", + "total_loan_repayment", + "net_pay_info", + "net_pay", + "column_break_53", + "rounded_total", + "section_break_55", + "total_in_words", + "amended_from" + ], + "fields": [ + { + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Posting Date", + "reqd": 1 + }, + { + "fieldname": "employee", + "fieldtype": "Link", + "in_global_search": 1, + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Employee", + "oldfieldname": "employee", + "oldfieldtype": "Link", + "options": "Employee", + "reqd": 1, + "search_index": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Read Only", + "in_global_search": 1, + "in_list_view": 1, + "label": "Employee Name", + "oldfieldname": "employee_name", + "oldfieldtype": "Data", + "reqd": 1 + }, + { + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Department", + "oldfieldname": "department", + "oldfieldtype": "Link", + "options": "Department", + "read_only": 1 + }, + { + "depends_on": "eval:doc.designation", + "fetch_from": "employee.designation", + "fieldname": "designation", + "fieldtype": "Read Only", + "label": "Designation", + "oldfieldname": "designation", + "oldfieldtype": "Link" + }, + { + "fetch_from": "employee.branch", + "fieldname": "branch", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Branch", + "oldfieldname": "branch", + "oldfieldtype": "Link", + "options": "Branch", + "read_only": 1 + }, + { + "fieldname": "column_break1", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "width": "50%" + }, + { + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "options": "Draft\nSubmitted\nCancelled", + "read_only": 1 + }, + { + "fieldname": "journal_entry", + "fieldtype": "Link", + "label": "Journal Entry", + "options": "Journal Entry", + "read_only": 1 + }, + { + "fieldname": "payroll_entry", + "fieldtype": "Link", + "label": "Payroll Entry", + "options": "Payroll Entry", + "read_only": 1 + }, + { + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Company", + "options": "Company", + "remember_last_selected_value": 1, + "reqd": 1 + }, + { + "allow_on_submit": 1, + "fieldname": "letter_head", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Letter Head", + "options": "Letter Head", + "print_hide": 1 + }, + { + "fieldname": "section_break_10", + "fieldtype": "Section Break" + }, + { + "default": "0", + "fieldname": "salary_slip_based_on_timesheet", + "fieldtype": "Check", + "label": "Salary Slip Based on Timesheet", + "read_only": 1 + }, + { + "fieldname": "start_date", + "fieldtype": "Date", + "label": "Start Date" + }, + { + "fieldname": "end_date", + "fieldtype": "Date", + "label": "End Date" + }, + { + "fieldname": "column_break_15", + "fieldtype": "Column Break" + }, + { + "fieldname": "salary_structure", + "fieldtype": "Link", + "label": "Salary Structure", + "options": "Salary Structure", + "read_only": 1 + }, + { + "depends_on": "eval:(!doc.salary_slip_based_on_timesheet)", + "fieldname": "payroll_frequency", + "fieldtype": "Select", + "label": "Payroll Frequency", + "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily" + }, + { + "fieldname": "total_working_days", + "fieldtype": "Float", + "label": "Working Days", + "oldfieldname": "total_days_in_month", + "oldfieldtype": "Int", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "leave_without_pay", + "fieldtype": "Float", + "label": "Leave Without Pay", + "oldfieldname": "leave_without_pay", + "oldfieldtype": "Currency" + }, + { + "fieldname": "payment_days", + "fieldtype": "Float", + "label": "Payment Days", + "oldfieldname": "payment_days", + "oldfieldtype": "Float", + "read_only": 1, + "reqd": 1 + }, + { + "fieldname": "hourly_wages", + "fieldtype": "Section Break" + }, + { + "fieldname": "timesheets", + "fieldtype": "Table", + "label": "Salary Slip Timesheet", + "options": "Salary Slip Timesheet" + }, + { + "fieldname": "column_break_20", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_working_hours", + "fieldtype": "Float", + "label": "Total Working Hours", + "print_hide_if_no_value": 1 + }, + { + "fieldname": "hour_rate", + "fieldtype": "Currency", + "label": "Hour Rate", + "options": "Company:company:default_currency", + "print_hide_if_no_value": 1 + }, + { + "fieldname": "section_break_26", + "fieldtype": "Section Break" + }, + { + "fieldname": "bank_name", + "fieldtype": "Data", + "label": "Bank Name", + "oldfieldname": "bank_name", + "oldfieldtype": "Data", + "read_only": 1 + }, + { + "fieldname": "bank_account_no", + "fieldtype": "Data", + "label": "Bank Account No.", + "oldfieldname": "bank_account_no", + "oldfieldtype": "Data", + "read_only": 1 + }, + { + "fieldname": "section_break_32", + "fieldtype": "Section Break" + }, + { + "default": "0", + "fieldname": "deduct_tax_for_unclaimed_employee_benefits", + "fieldtype": "Check", + "label": "Deduct Tax For Unclaimed Employee Benefits" + }, + { + "default": "0", + "fieldname": "deduct_tax_for_unsubmitted_tax_exemption_proof", + "fieldtype": "Check", + "label": "Deduct Tax For Unsubmitted Tax Exemption Proof" + }, + { + "fieldname": "earning_deduction", + "fieldtype": "Section Break", + "label": "Earning & Deduction", + "oldfieldtype": "Section Break" + }, + { + "fieldname": "earning", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "width": "50%" + }, + { + "fieldname": "earnings", + "fieldtype": "Table", + "label": "Earnings", + "oldfieldname": "earning_details", + "oldfieldtype": "Table", + "options": "Salary Detail" + }, + { + "fieldname": "deduction", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "width": "50%" + }, + { + "fieldname": "deductions", + "fieldtype": "Table", + "label": "Deductions", + "oldfieldname": "deduction_details", + "oldfieldtype": "Table", + "options": "Salary Detail" + }, + { + "fieldname": "totals", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break" + }, + { + "fieldname": "gross_pay", + "fieldtype": "Currency", + "label": "Gross Pay", + "oldfieldname": "gross_pay", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "column_break_25", + "fieldtype": "Column Break" + }, + { + "fieldname": "total_deduction", + "fieldtype": "Currency", + "label": "Total Deduction", + "oldfieldname": "total_deduction", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "depends_on": "total_loan_repayment", + "fieldname": "loan_repayment", + "fieldtype": "Section Break", + "label": "Loan repayment" + }, + { + "fieldname": "loans", + "fieldtype": "Table", + "label": "Employee Loan", + "options": "Salary Slip Loan", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "section_break_43", + "fieldtype": "Section Break" + }, + { + "default": "0", + "fieldname": "total_principal_amount", + "fieldtype": "Currency", + "label": "Total Principal Amount", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "total_interest_amount", + "fieldtype": "Currency", + "label": "Total Interest Amount", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "column_break_45", + "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "total_loan_repayment", + "fieldtype": "Currency", + "label": "Total Loan Repayment", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "net_pay_info", + "fieldtype": "Section Break", + "label": "net pay info" + }, + { + "description": "Gross Pay - Total Deduction - Loan Repayment", + "fieldname": "net_pay", + "fieldtype": "Currency", + "label": "Net Pay", + "oldfieldname": "net_pay", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "column_break_53", + "fieldtype": "Column Break" + }, + { + "bold": 1, + "fieldname": "rounded_total", + "fieldtype": "Currency", + "label": "Rounded Total", + "options": "Company:company:default_currency", + "read_only": 1 + }, + { + "fieldname": "section_break_55", + "fieldtype": "Section Break" + }, + { + "description": "Net Pay (in words) will be visible once you save the Salary Slip.", + "fieldname": "total_in_words", + "fieldtype": "Data", + "label": "Total in words", + "oldfieldname": "net_pay_in_words", + "oldfieldtype": "Data", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Amended From", + "no_copy": 1, + "oldfieldname": "amended_from", + "oldfieldtype": "Data", + "options": "Salary Slip", + "print_hide": 1, + "read_only": 1 + } + ], + "icon": "fa fa-file-text", + "idx": 9, + "is_submittable": 1, + "links": [], + "modified": "2019-12-31 17:13:45.146271", + "modified_by": "Administrator", + "module": "HR", + "name": "Salary Slip", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "read": 1, + "role": "Employee" + } + ], + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "timeline_field": "employee", + "title_field": "employee_name" +} \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py index 46be4fe2873..ea531cbf9b6 100644 --- a/erpnext/hr/doctype/salary_slip/salary_slip.py +++ b/erpnext/hr/doctype/salary_slip/salary_slip.py @@ -50,7 +50,8 @@ class SalarySlip(TransactionBase): self.calculate_net_pay() company_currency = erpnext.get_company_currency(self.company) - self.total_in_words = money_in_words(self.rounded_total, company_currency) + total = self.net_pay if self.is_rounding_total_disabled() else self.rounded_total + self.total_in_words = money_in_words(total, company_currency) if frappe.db.get_single_value("HR Settings", "max_working_hours_against_timesheet"): max_working_hours = frappe.db.get_single_value("HR Settings", "max_working_hours_against_timesheet") @@ -90,6 +91,9 @@ class SalarySlip(TransactionBase): if date_diff(self.end_date, self.start_date) < 0: frappe.throw(_("To date cannot be before From date")) + def is_rounding_total_disabled(self): + return cint(frappe.db.get_single_value("HR Settings", "disable_rounded_total")) + def check_existing(self): if not self.salary_slip_based_on_timesheet: ret_exist = frappe.db.sql("""select name from `tabSalary Slip` @@ -360,11 +364,11 @@ class SalarySlip(TransactionBase): return amount except NameError as err: - frappe.throw(_("Name error: {0}".format(err))) + frappe.throw(_("Name error: {0}").format(err)) except SyntaxError as err: - frappe.throw(_("Syntax error in formula or condition: {0}".format(err))) + frappe.throw(_("Syntax error in formula or condition: {0}").format(err)) except Exception as e: - frappe.throw(_("Error in formula or condition: {0}".format(e))) + frappe.throw(_("Error in formula or condition: {0}").format(e)) raise def add_employee_benefits(self, payroll_period): @@ -701,11 +705,11 @@ class SalarySlip(TransactionBase): if condition: return frappe.safe_eval(condition, self.whitelisted_globals, data) except NameError as err: - frappe.throw(_("Name error: {0}".format(err))) + frappe.throw(_("Name error: {0}").format(err)) except SyntaxError as err: - frappe.throw(_("Syntax error in condition: {0}".format(err))) + frappe.throw(_("Syntax error in condition: {0}").format(err)) except Exception as e: - frappe.throw(_("Error in formula or condition: {0}".format(e))) + frappe.throw(_("Error in formula or condition: {0}").format(e)) raise def get_salary_slip_row(self, salary_component): diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.js b/erpnext/hr/doctype/salary_structure/salary_structure.js index dd34ef2ae2c..71204481006 100755 --- a/erpnext/hr/doctype/salary_structure/salary_structure.js +++ b/erpnext/hr/doctype/salary_structure/salary_structure.js @@ -46,7 +46,7 @@ frappe.ui.form.on('Salary Structure', { frm.trigger("toggle_fields"); frm.fields_dict['earnings'].grid.set_column_disp("default_amount", false); frm.fields_dict['deductions'].grid.set_column_disp("default_amount", false); - + if(frm.doc.docstatus === 1) { frm.add_custom_button(__("Preview Salary Slip"), function() { frm.trigger('preview_salary_slip'); @@ -75,6 +75,7 @@ frappe.ui.form.on('Salary Structure', { title: __("Assign to Employees"), fields: [ {fieldname: "sec_break", fieldtype: "Section Break", label: __("Filter Employees By (Optional)")}, + {fieldname: "company", fieldtype: "Link", options: "Company", label: __("Company"), default: frm.doc.company, read_only:1}, {fieldname: "grade", fieldtype: "Link", options: "Employee Grade", label: __("Employee Grade")}, {fieldname:'department', fieldtype:'Link', options: 'Department', label: __('Department')}, {fieldname:'designation', fieldtype:'Link', options: 'Designation', label: __('Designation')}, @@ -87,7 +88,6 @@ frappe.ui.form.on('Salary Structure', { ], primary_action: function() { var data = d.get_values(); - frappe.call({ doc: frm.doc, method: "assign_salary_structure", @@ -119,47 +119,52 @@ frappe.ui.form.on('Salary Structure', { }, callback: function(r) { var employees = r.message; - var d = new frappe.ui.Dialog({ - title: __("Preview Salary Slip"), - fields: [ - { - "label":__("Employee"), - "fieldname":"employee", - "fieldtype":"Select", - "reqd": true, - options: employees - }, { - fieldname:"fetch", - "label":__("Show Salary Slip"), - "fieldtype":"Button" - } - ] - }); - d.get_input("fetch").on("click", function() { - var values = d.get_values(); - if(!values) return; - var print_format; - frm.doc.salary_slip_based_on_timesheet ? - print_format="Salary Slip based on Timesheet" : - print_format="Salary Slip Standard"; - - frappe.call({ - method: "erpnext.hr.doctype.salary_structure.salary_structure.make_salary_slip", - args: { - source_name: frm.doc.name, - employee: values.employee, - as_print: 1, - print_format: print_format, - for_preview: 1 - }, - callback: function(r) { - var new_window = window.open(); - new_window.document.write(r.message); - // frappe.msgprint(r.message); - } + if(!employees) return; + if (employees.length == 1){ + frm.events.open_salary_slip(frm, employees[0]); + } else { + var d = new frappe.ui.Dialog({ + title: __("Preview Salary Slip"), + fields: [ + { + "label":__("Employee"), + "fieldname":"employee", + "fieldtype":"Select", + "reqd": true, + options: employees + }, { + fieldname:"fetch", + "label":__("Show Salary Slip"), + "fieldtype":"Button" + } + ] }); - }); - d.show(); + d.get_input("fetch").on("click", function() { + var values = d.get_values(); + if(!values) return; + frm.events.open_salary_slip(frm, values.employee) + + }); + d.show(); + } + } + }); + }, + + open_salary_slip: function(frm, employee){ + var print_format = frm.doc.salary_slip_based_on_timesheet ? "Salary Slip based on Timesheet" : "Salary Slip Standard"; + frappe.call({ + method: "erpnext.hr.doctype.salary_structure.salary_structure.make_salary_slip", + args: { + source_name: frm.doc.name, + employee: employee, + as_print: 1, + print_format: print_format, + for_preview: 1 + }, + callback: function(r) { + var new_window = window.open(); + new_window.document.write(r.message); } }); }, diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.json b/erpnext/hr/doctype/salary_structure/salary_structure.json index 0e47278a3e1..58c4044093e 100644 --- a/erpnext/hr/doctype/salary_structure/salary_structure.json +++ b/erpnext/hr/doctype/salary_structure/salary_structure.json @@ -1,1010 +1,283 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "Prompt", - "beta": 0, - "creation": "2013-03-07 18:50:29", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 0, + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2013-03-07 18:50:29", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "company", + "letter_head", + "column_break1", + "is_active", + "payroll_frequency", + "is_default", + "time_sheet_earning_detail", + "salary_slip_based_on_timesheet", + "column_break_17", + "salary_component", + "hour_rate", + "leave_encashment_amount_per_day", + "max_benefits", + "earning_deduction", + "earning", + "earnings", + "deduction", + "deductions", + "net_pay_detail", + "column_break2", + "total_earning", + "total_deduction", + "net_pay", + "account", + "mode_of_payment", + "column_break_28", + "payment_account", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "remember_last_selected_value": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "letter_head", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Letter Head", - "length": 0, - "no_copy": 0, - "options": "Letter Head", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "letter_head", + "fieldtype": "Link", + "label": "Letter Head", + "options": "Letter Head" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break1", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "column_break1", + "fieldtype": "Column Break", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Yes", - "fieldname": "is_active", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Is Active", - "length": 0, - "no_copy": 0, - "oldfieldname": "is_active", - "oldfieldtype": "Select", - "options": "\nYes\nNo", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Yes", + "fieldname": "is_active", + "fieldtype": "Select", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Is Active", + "oldfieldname": "is_active", + "oldfieldtype": "Select", + "options": "\nYes\nNo", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Monthly", - "depends_on": "eval:(!doc.salary_slip_based_on_timesheet)", - "fieldname": "payroll_frequency", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payroll Frequency", - "length": 0, - "no_copy": 0, - "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Monthly", + "depends_on": "eval:(!doc.salary_slip_based_on_timesheet)", + "fieldname": "payroll_frequency", + "fieldtype": "Select", + "label": "Payroll Frequency", + "options": "\nMonthly\nFortnightly\nBimonthly\nWeekly\nDaily" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "No", - "fieldname": "is_default", - "fieldtype": "Select", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Default", - "length": 0, - "no_copy": 1, - "options": "Yes\nNo", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "No", + "fieldname": "is_default", + "fieldtype": "Select", + "hidden": 1, + "label": "Is Default", + "no_copy": 1, + "options": "Yes\nNo", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "time_sheet_earning_detail", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "time_sheet_earning_detail", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "salary_slip_based_on_timesheet", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Salary Slip Based on Timesheet", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "salary_slip_based_on_timesheet", + "fieldtype": "Check", + "label": "Salary Slip Based on Timesheet" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_17", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "description": "Salary Component for timesheet based payroll.", - "fieldname": "salary_component", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Salary Component", - "length": 0, - "no_copy": 0, - "options": "Salary Component", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "Salary Component for timesheet based payroll.", + "fieldname": "salary_component", + "fieldtype": "Link", + "label": "Salary Component", + "options": "Salary Component" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "hour_rate", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Hour Rate", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "hour_rate", + "fieldtype": "Currency", + "label": "Hour Rate", + "options": "Company:company:default_currency" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "leave_encashment_amount_per_day", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Leave Encashment Amount Per Day", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "leave_encashment_amount_per_day", + "fieldtype": "Currency", + "label": "Leave Encashment Amount Per Day", + "options": "Company:company:default_currency" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "max_benefits", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Max Benefits (Amount)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "max_benefits", + "fieldtype": "Currency", + "label": "Max Benefits (Amount)", + "options": "Company:company:default_currency" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "description": "Salary breakup based on Earning and Deduction.", - "fieldname": "earning_deduction", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "oldfieldname": "earning_deduction", - "oldfieldtype": "Section Break", - "permlevel": 0, - "precision": "2", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "Salary breakup based on Earning and Deduction.", + "fieldname": "earning_deduction", + "fieldtype": "Section Break", + "oldfieldname": "earning_deduction", + "oldfieldtype": "Section Break", + "precision": "2" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "earning", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Earning", - "length": 0, - "no_copy": 0, - "oldfieldname": "col_brk2", - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "earning", + "fieldtype": "Section Break", + "label": "Earning", + "oldfieldname": "col_brk2", + "oldfieldtype": "Column Break", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "earnings", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Earnings", - "length": 0, - "no_copy": 0, - "oldfieldname": "earning_details", - "oldfieldtype": "Table", - "options": "Salary Detail", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "earnings", + "fieldtype": "Table", + "label": "Earnings", + "oldfieldname": "earning_details", + "oldfieldtype": "Table", + "options": "Salary Detail" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "deduction", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Deduction", - "length": 0, - "no_copy": 0, - "oldfieldname": "col_brk3", - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "deduction", + "fieldtype": "Section Break", + "label": "Deduction", + "oldfieldname": "col_brk3", + "oldfieldtype": "Column Break", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "deductions", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Deductions", - "length": 0, - "no_copy": 0, - "oldfieldname": "deduction_details", - "oldfieldtype": "Table", - "options": "Salary Detail", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "deductions", + "fieldtype": "Table", + "label": "Deductions", + "oldfieldname": "deduction_details", + "oldfieldtype": "Table", + "options": "Salary Detail" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "net_pay_detail", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "options": "Simple", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "net_pay_detail", + "fieldtype": "Section Break", + "options": "Simple" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "column_break2", + "fieldtype": "Column Break", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_earning", - "fieldtype": "Currency", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Earning", - "length": 0, - "no_copy": 0, - "oldfieldname": "total_earning", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_earning", + "fieldtype": "Currency", + "hidden": 1, + "label": "Total Earning", + "oldfieldname": "total_earning", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_deduction", - "fieldtype": "Currency", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Deduction", - "length": 0, - "no_copy": 0, - "oldfieldname": "total_deduction", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_deduction", + "fieldtype": "Currency", + "hidden": 1, + "label": "Total Deduction", + "oldfieldname": "total_deduction", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "net_pay", - "fieldtype": "Currency", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Net Pay", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "net_pay", + "fieldtype": "Currency", + "hidden": 1, + "label": "Net Pay", + "options": "Company:company:default_currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "account", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Account", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "account", + "fieldtype": "Section Break", + "label": "Account" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "mode_of_payment", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Mode of Payment", - "length": 0, - "no_copy": 0, - "options": "Mode of Payment", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "mode_of_payment", + "fieldtype": "Link", + "label": "Mode of Payment", + "options": "Mode of Payment" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_28", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_28", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "payment_account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Payment Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "payment_account", + "fieldtype": "Link", + "label": "Payment Account", + "options": "Account" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Salary Structure", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Salary Structure", + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-file-text", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-02-18 18:51:53.932518", - "modified_by": "Administrator", - "module": "HR", - "name": "Salary Structure", - "owner": "Administrator", + ], + "icon": "fa fa-file-text", + "idx": 1, + "is_submittable": 1, + "links": [], + "modified": "2019-12-31 16:34:35.087658", + "modified_by": "Administrator", + "module": "HR", + "name": "Salary Structure", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 1, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "timeline_field": "", - "title_field": "", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.py b/erpnext/hr/doctype/salary_structure/salary_structure.py index 0e1a74f3702..568277f8a73 100644 --- a/erpnext/hr/doctype/salary_structure/salary_structure.py +++ b/erpnext/hr/doctype/salary_structure/salary_structure.py @@ -81,24 +81,24 @@ class SalaryStructure(Document): return employees @frappe.whitelist() - def assign_salary_structure(self, grade=None, department=None, designation=None,employee=None, + def assign_salary_structure(self, company=None, grade=None, department=None, designation=None,employee=None, from_date=None, base=None,variable=None): - employees = self.get_employees(grade= grade,department= department,designation= designation,name=employee) + employees = self.get_employees(company= company, grade= grade,department= department,designation= designation,name=employee) if employees: if len(employees) > 20: frappe.enqueue(assign_salary_structure_for_employees, timeout=600, employees=employees, salary_structure=self,from_date=from_date, base=base,variable=variable) else: - assign_salary_structure_for_employees(employees, self,from_date=from_date, base=base,variable=variable) + assign_salary_structure_for_employees(employees, self, from_date=from_date, base=base,variable=variable) else: frappe.msgprint(_("No Employee Found")) -def assign_salary_structure_for_employees(employees, salary_structure,from_date=None, base=None,variable=None): +def assign_salary_structure_for_employees(employees, salary_structure, from_date=None, base=None,variable=None): salary_structures_assignments = [] - existing_assignments_for = get_existing_assignments(employees, salary_structure.name,from_date) + existing_assignments_for = get_existing_assignments(employees, salary_structure, from_date) count=0 for employee in employees: if employee in existing_assignments_for: @@ -117,6 +117,7 @@ def create_salary_structures_assignment(employee, salary_structure, from_date, b assignment = frappe.new_doc("Salary Structure Assignment") assignment.employee = employee assignment.salary_structure = salary_structure.name + assignment.company = salary_structure.company assignment.from_date = from_date assignment.base = base assignment.variable = variable @@ -125,12 +126,12 @@ def create_salary_structures_assignment(employee, salary_structure, from_date, b return assignment.name -def get_existing_assignments(employees, salary_structure,from_date): +def get_existing_assignments(employees, salary_structure, from_date): salary_structures_assignments = frappe.db.sql_list(""" select distinct employee from `tabSalary Structure Assignment` where salary_structure=%s and employee in (%s) - and from_date=%s and docstatus=1 - """ % ('%s', ', '.join(['%s']*len(employees)),'%s'), [salary_structure] + employees+[from_date]) + and from_date=%s and company= %s and docstatus=1 + """ % ('%s', ', '.join(['%s']*len(employees)),'%s', '%s'), [salary_structure.name] + employees+[from_date]+[salary_structure.company]) if salary_structures_assignments: frappe.msgprint(_("Skipping Salary Structure Assignment for the following employees, as Salary Structure Assignment records already exists against them. {0}") .format("\n".join(salary_structures_assignments))) @@ -170,7 +171,7 @@ def make_salary_slip(source_name, target_doc = None, employee = None, as_print = def get_employees(salary_structure): employees = frappe.get_list('Salary Structure Assignment', filters={'salary_structure': salary_structure, 'docstatus': 1}, fields=['employee']) - + if not employees: frappe.throw(_("There's no Employee with Salary Structure: {0}. \ Assign {1} to an Employee to preview Salary Slip").format(salary_structure, salary_structure)) diff --git a/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.json b/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.json index 55e2df950b7..380c889477f 100644 --- a/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.json +++ b/erpnext/hr/doctype/salary_structure_assignment/salary_structure_assignment.json @@ -1,524 +1,170 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "HR-SSA-.YY.-.MM.-.#####", - "beta": 0, - "creation": "2018-04-13 16:38:41.769237", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_import": 1, + "autoname": "HR-SSA-.YY.-.MM.-.#####", + "creation": "2018-04-13 16:38:41.769237", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "employee", + "employee_name", + "department", + "designation", + "column_break_6", + "salary_structure", + "from_date", + "company", + "section_break_7", + "base", + "column_break_9", + "variable", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "employee", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Employee", + "options": "Employee", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.employee_name", - "fieldname": "employee_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Employee Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Data", + "label": "Employee Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.department", - "fieldname": "department", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Department", - "length": 0, - "no_copy": 0, - "options": "Department", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "employee.department", + "fieldname": "department", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Department", + "options": "Department", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "employee.designation", - "fieldname": "designation", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Designation", - "length": 0, - "no_copy": 0, - "options": "Designation", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "employee.designation", + "fieldname": "designation", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Designation", + "options": "Designation", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_6", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "salary_structure", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Salary Structure", - "length": 0, - "no_copy": 0, - "options": "Salary Structure", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "salary_structure", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Salary Structure", + "options": "Salary Structure", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "from_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "From Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "read_only": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_7", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_7", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Base", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "base", + "fieldtype": "Currency", + "label": "Base", + "options": "Company:company:default_currency" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_9", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "variable", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Variable", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "variable", + "fieldtype": "Currency", + "label": "Variable", + "options": "Company:company:default_currency" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Salary Structure Assignment", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Salary Structure Assignment", + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-08-21 16:15:48.755450", - "modified_by": "Administrator", - "module": "HR", - "name": "Salary Structure Assignment", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "links": [], + "modified": "2019-12-31 16:35:34.415099", + "modified_by": "Administrator", + "module": "HR", + "name": "Salary Structure Assignment", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR Manager", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "HR User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "create": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "HR User", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "employee_name", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "employee_name", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/hr/doctype/shift_assignment/shift_assignment.json b/erpnext/hr/doctype/shift_assignment/shift_assignment.json index d4cd1c4c939..72cbba8a0dc 100644 --- a/erpnext/hr/doctype/shift_assignment/shift_assignment.json +++ b/erpnext/hr/doctype/shift_assignment/shift_assignment.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "autoname": "HR-SHA-.YY.-.MM.-.#####", "creation": "2018-04-13 16:25:04.562730", @@ -8,12 +9,12 @@ "field_order": [ "employee", "employee_name", - "department", "shift_type", "column_break_3", "company", "date", "shift_request", + "department", "amended_from" ], "fields": [ @@ -82,7 +83,8 @@ } ], "is_submittable": 1, - "modified": "2019-05-30 15:40:54.418427", + "links": [], + "modified": "2019-12-12 15:49:06.956901", "modified_by": "Administrator", "module": "HR", "name": "Shift Assignment", diff --git a/erpnext/hr/doctype/shift_type/shift_type.py b/erpnext/hr/doctype/shift_type/shift_type.py index 8de92b2761a..49884103e21 100644 --- a/erpnext/hr/doctype/shift_type/shift_type.py +++ b/erpnext/hr/doctype/shift_type/shift_type.py @@ -11,7 +11,7 @@ from frappe.model.document import Document from frappe.utils import cint, getdate, get_datetime from erpnext.hr.doctype.shift_assignment.shift_assignment import get_actual_start_end_datetime_of_shift, get_employee_shift from erpnext.hr.doctype.employee_checkin.employee_checkin import mark_attendance_and_link_log, calculate_working_hours -from erpnext.hr.doctype.attendance.attendance import mark_absent +from erpnext.hr.doctype.attendance.attendance import mark_attendance from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee class ShiftType(Document): @@ -35,7 +35,7 @@ class ShiftType(Document): def get_attendance(self, logs): """Return attendance_status, working_hours for a set of logs belonging to a single shift. - Assumtion: + Assumtion: 1. These logs belongs to an single shift, single employee and is not in a holiday date. 2. Logs are in chronological order """ @@ -43,10 +43,10 @@ class ShiftType(Document): total_working_hours, in_time, out_time = calculate_working_hours(logs, self.determine_check_in_and_check_out, self.working_hours_calculation_based_on) if cint(self.enable_entry_grace_period) and in_time and in_time > logs[0].shift_start + timedelta(minutes=cint(self.late_entry_grace_period)): late_entry = True - + if cint(self.enable_exit_grace_period) and out_time and out_time < logs[0].shift_end - timedelta(minutes=cint(self.early_exit_grace_period)): early_exit = True - + if self.working_hours_threshold_for_absent and total_working_hours < self.working_hours_threshold_for_absent: return 'Absent', total_working_hours, late_entry, early_exit if self.working_hours_threshold_for_half_day and total_working_hours < self.working_hours_threshold_for_half_day: @@ -75,7 +75,7 @@ class ShiftType(Document): for date in dates: shift_details = get_employee_shift(employee, date, True) if shift_details and shift_details.shift_type.name == self.name: - mark_absent(employee, date, self.name) + mark_attendance(employee, date, self.name, 'Absent') def get_assigned_employee(self, from_date=None, consider_default_shift=False): filters = {'date':('>=', from_date), 'shift_type': self.name, 'docstatus': '1'} @@ -107,15 +107,15 @@ def get_filtered_date_list(employee, start_date, end_date, filter_attendance=Tru condition_query = '' if filter_attendance: condition_query += """ and a.selected_date not in ( - select attendance_date from `tabAttendance` - where docstatus = '1' and employee = %(employee)s + select attendance_date from `tabAttendance` + where docstatus = 1 and employee = %(employee)s and attendance_date between %(start_date)s and %(end_date)s)""" if holiday_list: condition_query += """ and a.selected_date not in ( select holiday_date from `tabHoliday` where parenttype = 'Holiday List' and parentfield = 'holidays' and parent = %(holiday_list)s and holiday_date between %(start_date)s and %(end_date)s)""" - + dates = frappe.db.sql("""select * from ({base_dates_query}) as a where a.selected_date <= %(end_date)s {condition_query} diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.py b/erpnext/hr/doctype/staffing_plan/staffing_plan.py index 595bcaa8d4a..5b84d00bd6f 100644 --- a/erpnext/hr/doctype/staffing_plan/staffing_plan.py +++ b/erpnext/hr/doctype/staffing_plan/staffing_plan.py @@ -57,8 +57,8 @@ class StaffingPlan(Document): and sp.to_date >= %s and sp.from_date <= %s and sp.company = %s """, (staffing_plan_detail.designation, self.from_date, self.to_date, self.company)) if overlap and overlap [0][0]: - frappe.throw(_("Staffing Plan {0} already exist for designation {1}" - .format(overlap[0][0], staffing_plan_detail.designation))) + frappe.throw(_("Staffing Plan {0} already exist for designation {1}") + .format(overlap[0][0], staffing_plan_detail.designation)) def validate_with_parent_plan(self, staffing_plan_detail): if not frappe.get_cached_value('Company', self.company, "parent_company"): diff --git a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py b/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py index 9ba6d5e63f4..628255b11f5 100644 --- a/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py +++ b/erpnext/hr/doctype/staffing_plan/test_staffing_plan.py @@ -14,7 +14,7 @@ test_dependencies = ["Designation"] class TestStaffingPlan(unittest.TestCase): def test_staffing_plan(self): _set_up() - frappe.db.set_value("Company", "_Test Company", "is_group", 1) + frappe.db.set_value("Company", "_Test Company 3", "is_group", 1) if frappe.db.exists("Staffing Plan", "Test"): return staffing_plan = frappe.new_doc("Staffing Plan") @@ -36,7 +36,7 @@ class TestStaffingPlan(unittest.TestCase): if frappe.db.exists("Staffing Plan", "Test 1"): return staffing_plan = frappe.new_doc("Staffing Plan") - staffing_plan.company = "_Test Company" + staffing_plan.company = "_Test Company 3" staffing_plan.name = "Test 1" staffing_plan.from_date = nowdate() staffing_plan.to_date = add_days(nowdate(), 10) @@ -52,7 +52,7 @@ class TestStaffingPlan(unittest.TestCase): if frappe.db.exists("Staffing Plan", "Test"): return staffing_plan = frappe.new_doc("Staffing Plan") - staffing_plan.company = "_Test Company" + staffing_plan.company = "_Test Company 3" staffing_plan.name = "Test" staffing_plan.from_date = nowdate() staffing_plan.to_date = add_days(nowdate(), 10) @@ -87,10 +87,11 @@ def _set_up(): def make_company(): if frappe.db.exists("Company", "_Test Company 10"): return + company = frappe.new_doc("Company") company.company_name = "_Test Company 10" company.abbr = "_TC10" - company.parent_company = "_Test Company" + company.parent_company = "_Test Company 3" company.default_currency = "INR" company.country = "Pakistan" company.insert() \ No newline at end of file diff --git a/erpnext/hr/doctype/travel_request/travel_request.json b/erpnext/hr/doctype/travel_request/travel_request.json index c1c6524af35..441907c02d1 100644 --- a/erpnext/hr/doctype/travel_request/travel_request.json +++ b/erpnext/hr/doctype/travel_request/travel_request.json @@ -1,4 +1,5 @@ { + "actions": [], "autoname": "HR-TRQ-.YYYY.-.#####", "creation": "2018-05-15 06:32:33.950356", "doctype": "DocType", @@ -11,8 +12,6 @@ "column_break_2", "purpose_of_travel", "details_of_sponsor", - "section_break_4", - "description", "employee_details", "employee", "employee_name", @@ -23,6 +22,8 @@ "personal_id_type", "personal_id_number", "passport_number", + "section_break_4", + "description", "travel_itinerary", "itinerary", "costing_details", @@ -33,7 +34,8 @@ "event_details", "name_of_organizer", "address_of_organizer", - "other_details" + "other_details", + "amended_from" ], "fields": [ { @@ -213,7 +215,8 @@ } ], "is_submittable": 1, - "modified": "2019-05-25 23:15:00.609186", + "links": [], + "modified": "2019-12-12 18:42:26.451359", "modified_by": "Administrator", "module": "HR", "name": "Travel Request", diff --git a/erpnext/hr/notification/training_scheduled/training_scheduled.html b/erpnext/hr/notification/training_scheduled/training_scheduled.html index b1aeb2c8739..374038ac202 100644 --- a/erpnext/hr/notification/training_scheduled/training_scheduled.html +++ b/erpnext/hr/notification/training_scheduled/training_scheduled.html @@ -1,9 +1,44 @@ -

{{_("Training Event")}}

+ + + + + + + + +
+
+ {{_("Training Event:")}} {{ doc.event_name }} +
+
-

{{ doc.introduction }}

- -

{{_("Details")}}

-{{_("Event Name")}}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }} -
{{_("Event Location")}}: {{ doc.location }} -
{{_("Start Time")}}: {{ doc.start_time }} -
{{_("End Time")}}: {{ doc.end_time }} + + + + + + + + +
+
+ {{ doc.introduction }} +
    +
  • {{_("Event Location")}}: {{ doc.location }}
  • + {% set start = frappe.utils.get_datetime(doc.start_time) %} + {% set end = frappe.utils.get_datetime(doc.end_time) %} + {% if start.date() == end.date() %} +
  • {{_("Date")}}: {{ start.strftime("%A, %d %b %Y") }}
  • +
  • + {{_("Timing")}}: {{ start.strftime("%I:%M %p") + ' to ' + end.strftime("%I:%M %p") }} +
  • + {% else %} +
  • {{_("Start Time")}}: {{ start.strftime("%A, %d %b %Y at %I:%M %p") }} +
  • +
  • {{_("End Time")}}: {{ end.strftime("%A, %d %b %Y at %I:%M %p") }} +
  • + {% endif %} +
  • {{ _('Event Link') }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}
  • +
+
+
\ No newline at end of file diff --git a/erpnext/hr/notification/training_scheduled/training_scheduled.json b/erpnext/hr/notification/training_scheduled/training_scheduled.json index c07e1a6285b..966b8875723 100644 --- a/erpnext/hr/notification/training_scheduled/training_scheduled.json +++ b/erpnext/hr/notification/training_scheduled/training_scheduled.json @@ -1,5 +1,7 @@ { "attach_print": 0, + "channel": "Email", + "condition": "", "creation": "2017-08-11 03:13:40.519614", "days_in_advance": 0, "docstatus": 0, @@ -9,8 +11,8 @@ "event": "Submit", "idx": 0, "is_standard": 1, - "message": "

{{_(\"Training Event\")}}

\n\n

{{ doc.introduction }}

\n\n

{{_(\"Details\")}}

\n{{_(\"Event Name\")}}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}\n
{{_(\"Event Location\")}}: {{ doc.location }}\n
{{_(\"Start Time\")}}: {{ doc.start_time }}\n
{{_(\"End Time\")}}: {{ doc.end_time }}\n", - "modified": "2017-08-13 22:49:42.338881", + "message": "\n \n \n \n \n \n \n \n
\n
\n {{_(\"Training Event:\")}} {{ doc.event_name }}\n
\n
\n\n\n \n \n \n \n \n \n \n
\n
\n
    \n
  • {{ doc.introduction }}
  • \n
  • {{_(\"Event Location\")}}: {{ doc.location }}
  • \n {% set start = frappe.utils.get_datetime(doc.start_time) %}\n {% set end = frappe.utils.get_datetime(doc.end_time) %}\n {% if start.date() == end.date() %}\n
  • {{_(\"Date\")}}: {{ start.strftime(\"%A, %d %b %Y\") }}
  • \n
  • \n {{_(\"Timing\")}}: {{ start.strftime(\"%I:%M %p\") + ' to ' + end.strftime(\"%I:%M %p\") }}\n
  • \n {% else %}\n
  • {{_(\"Start Time\")}}: {{ start.strftime(\"%A, %d %b %Y at %I:%M %p\") }}\n
  • \n
  • {{_(\"End Time\")}}: {{ end.strftime(\"%A, %d %b %Y at %I:%M %p\") }}\n
  • \n {% endif %}\n
\n {{ _('Event Link') }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}\n
\n
", + "modified": "2019-11-29 15:38:31.805409", "modified_by": "Administrator", "module": "HR", "name": "Training Scheduled", diff --git a/erpnext/hr/notification/training_scheduled/training_scheduled.md b/erpnext/hr/notification/training_scheduled/training_scheduled.md index bcadf7df590..374038ac202 100644 --- a/erpnext/hr/notification/training_scheduled/training_scheduled.md +++ b/erpnext/hr/notification/training_scheduled/training_scheduled.md @@ -1,9 +1,44 @@ -

{{_("Training Event")}}

-

{{ message }}

+ + + + + + + + +
+
+ {{_("Training Event:")}} {{ doc.event_name }} +
+
-

{{_("Details")}}

-{{_("Event Name")}}: {{ name }} -
{{_("Event Location")}}: {{ location }} -
{{_("Start Time")}}: {{ start_time }} -
{{_("End Time")}}: {{ end_time }} -
{{_("Attendance")}}: {{ attendance }} + + + + + + + + +
+
+ {{ doc.introduction }} +
    +
  • {{_("Event Location")}}: {{ doc.location }}
  • + {% set start = frappe.utils.get_datetime(doc.start_time) %} + {% set end = frappe.utils.get_datetime(doc.end_time) %} + {% if start.date() == end.date() %} +
  • {{_("Date")}}: {{ start.strftime("%A, %d %b %Y") }}
  • +
  • + {{_("Timing")}}: {{ start.strftime("%I:%M %p") + ' to ' + end.strftime("%I:%M %p") }} +
  • + {% else %} +
  • {{_("Start Time")}}: {{ start.strftime("%A, %d %b %Y at %I:%M %p") }} +
  • +
  • {{_("End Time")}}: {{ end.strftime("%A, %d %b %Y at %I:%M %p") }} +
  • + {% endif %} +
  • {{ _('Event Link') }}: {{ frappe.utils.get_link_to_form(doc.doctype, doc.name) }}
  • +
+
+
\ No newline at end of file diff --git a/erpnext/hr/print_format/standard_appointment_letter/__init__.py b/erpnext/hr/print_format/standard_appointment_letter/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/hr/print_format/standard_appointment_letter/standard_appointment_letter.html b/erpnext/hr/print_format/standard_appointment_letter/standard_appointment_letter.html new file mode 100644 index 00000000000..d60582e1a18 --- /dev/null +++ b/erpnext/hr/print_format/standard_appointment_letter/standard_appointment_letter.html @@ -0,0 +1,38 @@ +{%- from "templates/print_formats/standard_macros.html" import add_header -%} + +

Appointment Letter

+
+
+ {{ doc.applicant_name }}, +
+
+ Date: {{ doc.appointment_date }} +
+
+
+ {{ doc.introduction }} +
+
+ +
+
+Your sincerely,
+For {{ doc.company }} +
+ +
+ {{ doc.closing_notes }} +
+ +
+ ________________
+ {{ doc.applicant_name }} +
\ No newline at end of file diff --git a/erpnext/hr/print_format/standard_appointment_letter/standard_appointment_letter.json b/erpnext/hr/print_format/standard_appointment_letter/standard_appointment_letter.json new file mode 100644 index 00000000000..1813e711f56 --- /dev/null +++ b/erpnext/hr/print_format/standard_appointment_letter/standard_appointment_letter.json @@ -0,0 +1,23 @@ +{ + "align_labels_right": 0, + "creation": "2019-12-26 15:22:44.200332", + "custom_format": 0, + "default_print_language": "en", + "disabled": 0, + "doc_type": "Appointment Letter", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "idx": 0, + "line_breaks": 0, + "modified": "2020-01-21 17:24:16.705082", + "modified_by": "Administrator", + "module": "HR", + "name": "Standard Appointment Letter", + "owner": "Administrator", + "print_format_builder": 0, + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 0, + "standard": "Yes" +} \ No newline at end of file diff --git a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py index 1e9c83bf3e6..b55b45fcdfd 100644 --- a/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py +++ b/erpnext/hr/report/monthly_attendance_sheet/monthly_attendance_sheet.py @@ -37,13 +37,22 @@ def execute(filters=None): total_p = total_a = total_l = 0.0 for day in range(filters["total_days_in_month"]): - status = att_map.get(emp).get(day + 1, "None") - status_map = {"Present": "P", "Absent": "A", "Half Day": "HD", "On Leave": "L", "None": "", "Holiday":"H"} - if status == "None" and holiday_map: + status = att_map.get(emp).get(day + 1) + status_map = { + "Absent": "A", + "Half Day": "HD", + "Holiday":"H", + "On Leave": "L", + "Present": "P", + "Work From Home": "WFH" + } + + if status is None and holiday_map: emp_holiday_list = emp_det.holiday_list if emp_det.holiday_list else default_holiday_list if emp_holiday_list in holiday_map and (day+1) in holiday_map[emp_holiday_list]: status = "Holiday" - row.append(status_map[status]) + + row.append(status_map.get(status, "")) if status == "Present": total_p += 1 @@ -66,7 +75,7 @@ def execute(filters=None): leave_details = frappe.db.sql("""select leave_type, status, count(*) as count from `tabAttendance`\ where leave_type is not NULL %s group by leave_type, status""" % conditions, filters, as_dict=1) - + time_default_counts = frappe.db.sql("""select (select count(*) from `tabAttendance` where \ late_entry = 1 %s) as late_entry_count, (select count(*) from tabAttendance where \ early_exit = 1 %s) as early_exit_count""" % (conditions, conditions), filters) @@ -85,7 +94,7 @@ def execute(filters=None): row.append(leaves[d]) else: row.append("0.0") - + row.extend([time_default_counts[0][0],time_default_counts[0][1]]) data.append(row) return columns, data diff --git a/erpnext/hr/utils.py b/erpnext/hr/utils.py index 1464a779368..c3e8d27557d 100644 --- a/erpnext/hr/utils.py +++ b/erpnext/hr/utils.py @@ -321,11 +321,11 @@ def allocate_earned_leaves(): if new_allocation == allocation.total_leaves_allocated: continue allocation.db_set("total_leaves_allocated", new_allocation, update_modified=False) - create_earned_leave_ledger_entry(allocation, earned_leaves, today) + create_additional_leave_ledger_entry(allocation, earned_leaves, today) -def create_earned_leave_ledger_entry(allocation, earned_leaves, date): - ''' Create leave ledger entry based on the earned leave frequency ''' - allocation.new_leaves_allocated = earned_leaves +def create_additional_leave_ledger_entry(allocation, leaves, date): + ''' Create leave ledger entry for leave types ''' + allocation.new_leaves_allocated = leaves allocation.from_date = date allocation.unused_leaves = 0 allocation.create_leave_ledger_entry() @@ -389,6 +389,7 @@ def get_sal_slip_total_benefit_given(employee, payroll_period, component=False): def get_holidays_for_employee(employee, start_date, end_date): holiday_list = get_holiday_list_for_employee(employee) + holidays = frappe.db.sql_list('''select holiday_date from `tabHoliday` where parent=%(holiday_list)s diff --git a/erpnext/hub_node/api.py b/erpnext/hub_node/api.py index 0d01c67650b..2035174c983 100644 --- a/erpnext/hub_node/api.py +++ b/erpnext/hub_node/api.py @@ -70,7 +70,7 @@ def map_fields(items): field_mappings = get_field_mappings() table_fields = [d.fieldname for d in frappe.get_meta('Item').get_table_fields()] - hub_seller_name = frappe.db.get_value('Marketplace Settings' , 'Marketplace Settings', 'hub_seller_name') + hub_seller_name = frappe.db.get_value('Marketplace Settings', 'Marketplace Settings', 'hub_seller_name') for item in items: for fieldname in table_fields: @@ -115,10 +115,21 @@ def get_valid_items(search_value=''): return valid_items +@frappe.whitelist() +def update_item(ref_doc, data): + data = json.loads(data) + + data.update(dict(doctype='Hub Item', name=ref_doc)) + try: + connection = get_hub_connection() + connection.update(data) + except Exception as e: + frappe.log_error(message=e, title='Hub Sync Error') @frappe.whitelist() def publish_selected_items(items_to_publish): items_to_publish = json.loads(items_to_publish) + items_to_update = [] if not len(items_to_publish): frappe.throw(_('No items to publish')) @@ -126,14 +137,24 @@ def publish_selected_items(items_to_publish): item_code = item.get('item_code') frappe.db.set_value('Item', item_code, 'publish_in_hub', 1) - frappe.get_doc({ + hub_dict = { 'doctype': 'Hub Tracked Item', 'item_code': item_code, + 'published': 1, 'hub_category': item.get('hub_category'), 'image_list': item.get('image_list') - }).insert(ignore_if_duplicate=True) + } + if frappe.db.exists('Hub Tracked Item', item_code): + items_to_update.append(item) + hub_tracked_item = frappe.get_doc('Hub Tracked Item', item_code) + hub_tracked_item.update(hub_dict) + hub_tracked_item.save() + else: + frappe.get_doc(hub_dict).insert(ignore_if_duplicate=True) - items = map_fields(items_to_publish) + items_to_publish = list(filter(lambda x: x not in items_to_update, items_to_publish)) + new_items = map_fields(items_to_publish) + existing_items = map_fields(items_to_update) try: item_sync_preprocess(len(items)) @@ -141,12 +162,26 @@ def publish_selected_items(items_to_publish): # TODO: Publish Progress connection = get_hub_connection() - connection.insert_many(items) + connection.insert_many(new_items) + connection.bulk_update(existing_items) item_sync_postprocess() except Exception as e: frappe.log_error(message=e, title='Hub Sync Error') +@frappe.whitelist() +def unpublish_item(item_code, hub_item_name): + ''' Remove item listing from the marketplace ''' + + response = call_hub_method('unpublish_item', { + 'hub_item_name': hub_item_name + }) + + if response: + frappe.db.set_value('Item', item_code, 'publish_in_hub', 0) + else: + frappe.throw(_('Unable to update remote activity')) + @frappe.whitelist() def get_unregistered_users(): settings = frappe.get_single('Marketplace Settings') diff --git a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.json b/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.json index 2e89887dacf..7d07ba40938 100644 --- a/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.json +++ b/erpnext/hub_node/doctype/hub_tracked_item/hub_tracked_item.json @@ -77,6 +77,38 @@ "translatable": 0, "unique": 0 }, + { + "allow_bulk_edit": 0, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "published", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Published", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "translatable": 0, + "unique": 0 + }, { "allow_bulk_edit": 0, "allow_in_quick_entry": 0, @@ -120,7 +152,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2018-09-10 11:37:35.951019", + "modified": "2019-12-10 11:37:35.951019", "modified_by": "Administrator", "module": "Hub Node", "name": "Hub Tracked Item", diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py index 94d85f77ef4..b58f999cfba 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py @@ -54,7 +54,7 @@ class MaintenanceSchedule(TransactionBase): email_map[d.sales_person] = sp.get_email_id() except frappe.ValidationError: no_email_sp.append(d.sales_person) - + if no_email_sp: frappe.msgprint( frappe._("Setting Events to {0}, since the Employee attached to the below Sales Persons does not have a User ID{1}").format( @@ -66,7 +66,7 @@ class MaintenanceSchedule(TransactionBase): parent=%s""", (d.sales_person, d.item_code, self.name), as_dict=1) for key in scheduled_date: - description =frappe._("Reference: {0}, Item Code: {1} and Customer: {2}").format(self.name, d.item_code, self.customer) + description =frappe._("Reference: {0}, Item Code: {1} and Customer: {2}").format(self.name, d.item_code, self.customer) frappe.get_doc({ "doctype": "Event", "owner": email_map.get(d.sales_person, self.owner), @@ -146,11 +146,11 @@ class MaintenanceSchedule(TransactionBase): if not d.item_code: throw(_("Please select item code")) elif not d.start_date or not d.end_date: - throw(_("Please select Start Date and End Date for Item {0}".format(d.item_code))) + throw(_("Please select Start Date and End Date for Item {0}").format(d.item_code)) elif not d.no_of_visits: throw(_("Please mention no of visits required")) elif not d.sales_person: - throw(_("Please select a Sales Person for item: {0}".format(d.item_name))) + throw(_("Please select a Sales Person for item: {0}").format(d.item_name)) if getdate(d.start_date) >= getdate(d.end_date): throw(_("Start date should be less than end date for Item {0}").format(d.item_code)) @@ -193,7 +193,7 @@ class MaintenanceSchedule(TransactionBase): if sr_details.amc_expiry_date and getdate(sr_details.amc_expiry_date) >= getdate(amc_start_date): throw(_("Serial No {0} is under maintenance contract upto {1}") - .format(serial_no, sr_details.amc_start_date)) + .format(serial_no, sr_details.amc_expiry_date)) if not sr_details.warehouse and sr_details.delivery_date and \ getdate(sr_details.delivery_date) >= getdate(amc_start_date): diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.json b/erpnext/manufacturing/doctype/blanket_order/blanket_order.json index 260e0b8a736..0330e5c85c9 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.json +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.json @@ -89,7 +89,8 @@ "fieldtype": "Link", "label": "Company", "options": "Company", - "reqd": 1 + "reqd": 1, + "search_index": 1 }, { "fieldname": "section_break_12", @@ -129,7 +130,7 @@ } ], "is_submittable": 1, - "modified": "2019-10-16 13:38:32.302316", + "modified": "2019-11-18 19:37:37.151686", "modified_by": "Administrator", "module": "Manufacturing", "name": "Blanket Order", diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py index faed707d600..38118bd78d4 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py @@ -44,6 +44,8 @@ def make_sales_order(source_name): target.item_name = item.get("item_name") target.description = item.get("description") target.uom = item.get("stock_uom") + target.against_blanket_order = 1 + target.blanket_order = source_name target_doc = get_mapped_doc("Blanket Order", source_name, { "Blanket Order": { @@ -71,6 +73,8 @@ def make_purchase_order(source_name): target.description = item.get("description") target.uom = item.get("stock_uom") target.warehouse = item.get("default_warehouse") + target.against_blanket_order = 1 + target.blanket_order = source_name target_doc = get_mapped_doc("Blanket Order", source_name, { "Blanket Order": { diff --git a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json index 099eed4aece..977ad547f55 100644 --- a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json +++ b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json @@ -1,298 +1,78 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2018-05-24 07:20:04.255236", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "creation": "2018-05-24 07:20:04.255236", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item_code", + "item_name", + "column_break_3", + "qty", + "rate", + "ordered_qty", + "section_break_7", + "terms_and_conditions" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "item_code", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Item Code", - "length": 0, - "no_copy": 0, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Code", + "options": "Item", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "item_code.item_name", - "fieldname": "item_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "item_code.item_name", + "fieldname": "item_name", + "fieldtype": "Data", + "label": "Item Name" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Quantity", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Quantity" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "rate", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Rate", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "rate", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Rate", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "ordered_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Ordered Quantity", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "ordered_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Ordered Quantity", + "no_copy": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_7", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_7", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "terms_and_conditions", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Terms and Conditions", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "terms_and_conditions", + "fieldtype": "Text", + "label": "Terms and Conditions" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-06-14 07:04:14.050836", - "modified_by": "Administrator", - "module": "Manufacturing", - "name": "Blanket Order Item", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "istable": 1, + "modified": "2019-11-18 19:37:46.245878", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Blanket Order Item", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index b9591d6054b..3acaee4ffbd 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -5,6 +5,11 @@ frappe.provide("erpnext.bom"); frappe.ui.form.on("BOM", { setup: function(frm) { + frm.custom_make_buttons = { + 'Work Order': 'Work Order', + 'Quality Inspection': 'Quality Inspection' + }; + frm.set_query("bom_no", "items", function() { return { filters: { @@ -85,9 +90,17 @@ frappe.ui.form.on("BOM", { } if(frm.doc.docstatus!=0) { - frm.add_custom_button(__("Duplicate"), function() { - frm.copy_doc(); - }); + frm.add_custom_button(__("Work Order"), function() { + frm.trigger("make_work_order"); + }, __("Create")); + + if (frm.doc.inspection_required) { + frm.add_custom_button(__("Quality Inspection"), function() { + frm.trigger("make_quality_inspection"); + }, __("Create")); + } + + frm.page.set_inner_btn_group_as_primary(__('Create')); } if(frm.doc.items && frm.doc.allow_alternative_item) { @@ -109,6 +122,41 @@ frappe.ui.form.on("BOM", { } }, + make_work_order: function(frm) { + const fields = [{ + fieldtype: 'Float', + label: __('Qty To Manufacture'), + fieldname: 'qty', + reqd: 1, + default: 1 + }]; + + frappe.prompt(fields, data => { + frappe.call({ + method: "erpnext.manufacturing.doctype.work_order.work_order.make_work_order", + args: { + item: frm.doc.item, + qty: data.qty || 0.0, + project: frm.doc.project + }, + freeze: true, + callback: function(r) { + if(r.message) { + var doc = frappe.model.sync(r.message)[0]; + frappe.set_route("Form", doc.doctype, doc.name); + } + } + }); + }, __("Enter Value"), __("Create")); + }, + + make_quality_inspection: function(frm) { + frappe.model.open_mapped_doc({ + method: "erpnext.stock.doctype.quality_inspection.quality_inspection.make_quality_inspection", + frm: frm + }) + }, + update_cost: function(frm) { return frappe.call({ doc: frm.doc, diff --git a/erpnext/manufacturing/doctype/bom/bom.json b/erpnext/manufacturing/doctype/bom/bom.json index a0faeb5fb55..63f4f977c59 100644 --- a/erpnext/manufacturing/doctype/bom/bom.json +++ b/erpnext/manufacturing/doctype/bom/bom.json @@ -3,33 +3,36 @@ "creation": "2013-01-22 15:11:38", "doctype": "DocType", "document_type": "Setup", + "engine": "InnoDB", "field_order": [ "item", - "item_name", - "image", - "uom", "quantity", + "set_rate_of_sub_assembly_item_based_on_bom", "cb0", "is_active", "is_default", - "with_operations", - "inspection_required", "allow_alternative_item", - "allow_same_item_multiple_times", - "set_rate_of_sub_assembly_item_based_on_bom", - "quality_inspection_template", + "image", + "item_name", + "uom", "currency_detail", "company", - "transfer_material_against", + "project", "conversion_rate", "column_break_12", "currency", "rm_cost_as_per", "buying_price_list", - "operations_section", + "section_break_21", + "with_operations", + "column_break_23", + "transfer_material_against", "routing", + "operations_section", "operations", "materials_section", + "inspection_required", + "quality_inspection_template", "items", "scrap_section", "scrap_items", @@ -41,14 +44,9 @@ "base_operating_cost", "base_raw_material_cost", "base_scrap_material_cost", - "total_cost_of_bom", - "total_cost", "column_break_26", + "total_cost", "base_total_cost", - "more_info_section", - "project", - "amended_from", - "col_break23", "section_break_25", "description", "column_break_27", @@ -57,12 +55,14 @@ "website_section", "show_in_website", "route", + "column_break_52", "website_image", "thumbnail", "sb_web_spec", - "web_long_description", "show_items", - "show_operations" + "show_operations", + "web_long_description", + "amended_from" ], "fields": [ { @@ -152,7 +152,7 @@ "default": "0", "fieldname": "inspection_required", "fieldtype": "Check", - "label": "Inspection Required" + "label": "Quality Inspection Required" }, { "default": "0", @@ -160,12 +160,6 @@ "fieldtype": "Check", "label": "Allow Alternative Item" }, - { - "default": "0", - "fieldname": "allow_same_item_multiple_times", - "fieldtype": "Check", - "label": "Allow Same Item Multiple Times" - }, { "allow_on_submit": 1, "default": "1", @@ -193,6 +187,7 @@ "reqd": 1 }, { + "default": "Work Order", "fieldname": "transfer_material_against", "fieldtype": "Select", "label": "Transfer Material Against", @@ -235,10 +230,10 @@ { "fieldname": "operations_section", "fieldtype": "Section Break", - "label": "Operations", "oldfieldtype": "Section Break" }, { + "depends_on": "with_operations", "fieldname": "routing", "fieldtype": "Link", "label": "Routing", @@ -335,10 +330,6 @@ "options": "Company:company:default_currency", "read_only": 1 }, - { - "fieldname": "total_cost_of_bom", - "fieldtype": "Section Break" - }, { "fieldname": "total_cost", "fieldtype": "Currency", @@ -359,10 +350,6 @@ "print_hide": 1, "read_only": 1 }, - { - "fieldname": "more_info_section", - "fieldtype": "Section Break" - }, { "fieldname": "project", "fieldtype": "Link", @@ -381,10 +368,6 @@ "print_hide": 1, "read_only": 1 }, - { - "fieldname": "col_break23", - "fieldtype": "Column Break" - }, { "fieldname": "section_break_25", "fieldtype": "Section Break" @@ -481,13 +464,26 @@ "fieldname": "show_operations", "fieldtype": "Check", "label": "Show Operations" + }, + { + "fieldname": "section_break_21", + "fieldtype": "Section Break", + "label": "Operations" + }, + { + "fieldname": "column_break_23", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_52", + "fieldtype": "Column Break" } ], "icon": "fa fa-sitemap", "idx": 1, "image_field": "image", "is_submittable": 1, - "modified": "2019-07-30 17:00:09.665068", + "modified": "2019-11-22 14:35:12.142150", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM", diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index db79d7feda4..7f8bd67a522 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -47,7 +47,18 @@ class BOM(WebsiteGenerator): else: idx = 1 - self.name = 'BOM-' + self.item + ('-%.3i' % idx) + name = 'BOM-' + self.item + ('-%.3i' % idx) + if frappe.db.exists("BOM", name): + conflicting_bom = frappe.get_doc("BOM", name) + + if conflicting_bom.item != self.item: + + frappe.throw(_("""A BOM with name {0} already exists for item {1}. +
Did you rename the item? Please contact Administrator / Tech support + """).format(frappe.bold(name), frappe.bold(conflicting_bom.item))) + + self.name = name + def validate(self): self.route = frappe.scrub(self.name).replace('_', '-') @@ -65,6 +76,7 @@ class BOM(WebsiteGenerator): context.parents = [{'name': 'boms', 'title': _('All BOMs') }] def on_update(self): + frappe.cache().hdel('bom_children', self.name) self.check_recursion() self.update_stock_qty() self.update_exploded_items() @@ -96,6 +108,7 @@ class BOM(WebsiteGenerator): def get_routing(self): if self.routing: + self.set("operations", []) for d in frappe.get_all("BOM Operation", fields = ["*"], filters = {'parenttype': 'Routing', 'parent': self.routing}): child = self.append('operations', d) @@ -289,7 +302,7 @@ class BOM(WebsiteGenerator): if not valuation_rate: valuation_rate = frappe.db.get_value("Item", args['item_code'], "valuation_rate") - return valuation_rate + return flt(valuation_rate) def manage_default_bom(self): """ Uncheck others if current one is selected as default or @@ -362,15 +375,9 @@ class BOM(WebsiteGenerator): def validate_materials(self): """ Validate raw material entries """ - def get_duplicates(lst): - seen = set() - seen_add = seen.add - for item in lst: - if item.item_code in seen or seen_add(item.item_code): - yield item - if not self.get('items'): frappe.throw(_("Raw Materials cannot be blank.")) + check_list = [] for m in self.get('items'): if m.bom_no: @@ -379,16 +386,6 @@ class BOM(WebsiteGenerator): frappe.throw(_("Quantity required for Item {0} in row {1}").format(m.item_code, m.idx)) check_list.append(m) - if not self.allow_same_item_multiple_times: - duplicate_items = list(get_duplicates(check_list)) - if duplicate_items: - li = [] - for i in duplicate_items: - li.append("{0} on row {1}".format(i.item_code, i.idx)) - duplicate_list = '
' + '
'.join(li) - - frappe.throw(_("Same item has been entered multiple times. {0}").format(duplicate_list)) - def check_recursion(self, bom_list=[]): """ Check whether recursion occurs in any bom""" bom_list = self.traverse_tree() @@ -602,7 +599,7 @@ class BOM(WebsiteGenerator): for d in self.operations: if not d.description: d.description = frappe.db.get_value('Operation', d.operation, 'description') - if not d.batch_size > 0: + if not d.batch_size or d.batch_size <= 0: d.batch_size = 1 def get_list_context(context): @@ -621,6 +618,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite item.image, bom.project, item.stock_uom, + item.item_group, item.allow_alternative_item, item_default.default_warehouse, item_default.expense_account as expense_account, @@ -647,7 +645,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite is_stock_item=is_stock_item, qty_field="stock_qty", select_columns = """, bom_item.source_warehouse, bom_item.operation, - bom_item.include_item_in_manufacturing, bom_item.description, + bom_item.include_item_in_manufacturing, bom_item.description, bom_item.rate, (Select idx from `tabBOM Item` where item_code = bom_item.item_code and parent = %(parent)s limit 1) as idx""") items = frappe.db.sql(query, { "parent": bom, "qty": qty, "bom": bom, "company": company }, as_dict=True) @@ -661,7 +659,7 @@ def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1, fetch_scrap_ite qty_field="stock_qty" if fetch_qty_in_stock_uom else "qty", select_columns = """, bom_item.uom, bom_item.conversion_factor, bom_item.source_warehouse, bom_item.idx, bom_item.operation, bom_item.include_item_in_manufacturing, - bom_item.description """) + bom_item.description, bom_item.base_rate as rate """) items = frappe.db.sql(query, { "qty": qty, "bom": bom, "company": company }, as_dict=True) for item in items: @@ -774,25 +772,57 @@ def get_boms_in_bottom_up_order(bom_no=None): def add_additional_cost(stock_entry, work_order): # Add non stock items cost in the additional cost - bom = frappe.get_doc('BOM', work_order.bom_no) - table = 'exploded_items' if work_order.get('use_multi_level_bom') else 'items' + stock_entry.additional_costs = [] expenses_included_in_valuation = frappe.get_cached_value("Company", work_order.company, "expenses_included_in_valuation") + add_non_stock_items_cost(stock_entry, work_order, expenses_included_in_valuation) + add_operations_cost(stock_entry, work_order, expenses_included_in_valuation) + +def add_non_stock_items_cost(stock_entry, work_order, expense_account): + bom = frappe.get_doc('BOM', work_order.bom_no) + table = 'exploded_items' if work_order.get('use_multi_level_bom') else 'items' + items = {} for d in bom.get(table): - items.setdefault(d.item_code, d.rate) + items.setdefault(d.item_code, d.amount) non_stock_items = frappe.get_all('Item', fields="name", filters={'name': ('in', list(items.keys())), 'ifnull(is_stock_item, 0)': 0}, as_list=1) + non_stock_items_cost = 0.0 for name in non_stock_items: + non_stock_items_cost += flt(items.get(name[0])) * flt(stock_entry.fg_completed_qty) / flt(bom.quantity) + + if non_stock_items_cost: stock_entry.append('additional_costs', { - 'expense_account': expenses_included_in_valuation, - 'description': name[0], - 'amount': items.get(name[0]) + 'expense_account': expense_account, + 'description': _("Non stock items"), + 'amount': non_stock_items_cost }) +def add_operations_cost(stock_entry, work_order=None, expense_account=None): + from erpnext.stock.doctype.stock_entry.stock_entry import get_operating_cost_per_unit + operating_cost_per_unit = get_operating_cost_per_unit(work_order, stock_entry.bom_no) + + if operating_cost_per_unit: + stock_entry.append('additional_costs', { + "expense_account": expense_account, + "description": _("Operating Cost as per Work Order / BOM"), + "amount": operating_cost_per_unit * flt(stock_entry.fg_completed_qty) + }) + + if work_order and work_order.additional_operating_cost and work_order.qty: + additional_operating_cost_per_unit = \ + flt(work_order.additional_operating_cost) / flt(work_order.qty) + + if additional_operating_cost_per_unit: + stock_entry.append('additional_costs', { + "expense_account": expense_account, + "description": "Additional Operating Cost", + "amount": additional_operating_cost_per_unit * flt(stock_entry.fg_completed_qty) + }) + @frappe.whitelist() def get_bom_diff(bom1, bom2): from frappe.model import table_fields diff --git a/erpnext/manufacturing/doctype/bom/bom_dashboard.py b/erpnext/manufacturing/doctype/bom/bom_dashboard.py index 803ece7c789..361826e2d0c 100644 --- a/erpnext/manufacturing/doctype/bom/bom_dashboard.py +++ b/erpnext/manufacturing/doctype/bom/bom_dashboard.py @@ -17,11 +17,13 @@ def get_data(): }, { 'label': _('Manufacture'), - 'items': ['BOM', 'Work Order', 'Job Card', 'Production Plan'] + 'items': ['BOM', 'Work Order', 'Job Card'] }, { - 'label': _('Purchase'), + 'label': _('Subcontract'), 'items': ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'] } - ] + ], + 'disable_create_buttons': ["Item", "Purchase Order", "Purchase Receipt", + "Purchase Invoice", "Job Card", "Stock Entry", "BOM"] } diff --git a/erpnext/manufacturing/doctype/bom_item/bom_item.json b/erpnext/manufacturing/doctype/bom_item/bom_item.json index febf315988c..f094be4c647 100644 --- a/erpnext/manufacturing/doctype/bom_item/bom_item.json +++ b/erpnext/manufacturing/doctype/bom_item/bom_item.json @@ -1,1053 +1,273 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, "creation": "2013-02-22 01:27:49", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", "editable_grid": 1, + "field_order": [ + "item_code", + "item_name", + "operation", + "column_break_3", + "bom_no", + "source_warehouse", + "allow_alternative_item", + "section_break_5", + "description", + "col_break1", + "image", + "image_view", + "quantity_and_rate", + "qty", + "uom", + "col_break2", + "stock_qty", + "stock_uom", + "conversion_factor", + "rate_amount_section", + "rate", + "base_rate", + "column_break_21", + "amount", + "base_amount", + "section_break_18", + "scrap", + "qty_consumed_per_unit", + "section_break_27", + "include_item_in_manufacturing", + "original_item" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, "columns": 3, "fieldname": "item_code", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Item Code", - "length": 0, - "no_copy": 0, "oldfieldname": "item_code", "oldfieldtype": "Link", "options": "Item", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "search_index": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, "columns": 3, "fieldname": "item_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Item Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Item Name" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "operation", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Item operation", - "length": 0, - "no_copy": 0, - "options": "Operation", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Operation" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "bom_no", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "BOM No", - "length": 0, - "no_copy": 0, "oldfieldname": "bom_no", "oldfieldtype": "Link", "options": "BOM", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "150px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "150px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "source_warehouse", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Source Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Warehouse" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, "fieldname": "section_break_5", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Description" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "description", "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Item Description", - "length": 0, - "no_copy": 0, "oldfieldname": "description", "oldfieldtype": "Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "250px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "250px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "col_break1", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "image", "fieldtype": "Attach", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Image", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "print_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "image_view", "fieldtype": "Image", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Image View", - "length": 0, - "no_copy": 0, - "options": "image", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "image" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "quantity_and_rate", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Quantity and Rate", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Quantity and Rate" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, "columns": 2, "fieldname": "qty", "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Qty", - "length": 0, - "no_copy": 0, "oldfieldname": "qty", "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, "columns": 1, "fieldname": "uom", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "UOM", - "length": 0, - "no_copy": 0, "options": "UOM", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "col_break2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "stock_qty", "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Stock Qty", - "length": 0, - "no_copy": 0, "oldfieldname": "stock_qty", "oldfieldtype": "Currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "stock_uom", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Stock UOM", - "length": 0, - "no_copy": 0, "oldfieldname": "stock_uom", "oldfieldtype": "Data", "options": "UOM", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "conversion_factor", "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Conversion Factor", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Conversion Factor" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "rate_amount_section", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rate & Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Rate & Amount" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", "fieldname": "rate", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Rate", - "length": 0, - "no_copy": 0, "options": "currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "base_rate", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Basic Rate (Company Currency)", - "length": 0, - "no_copy": 0, "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_21", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "amount", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Amount", - "length": 0, - "no_copy": 0, "oldfieldname": "amount_as_per_mar", "oldfieldtype": "Currency", "options": "currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, "print_width": "150px", "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, "width": "150px" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "base_amount", "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Amount (Company Currency)", - "length": 0, - "no_copy": 0, "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_18", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, "columns": 1, "fieldname": "scrap", "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Scrap %", - "length": 0, - "no_copy": 0, "oldfieldname": "scrap", "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "print_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "qty_consumed_per_unit", "fieldtype": "Float", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Qty Consumed Per Unit", - "length": 0, - "no_copy": 0, "oldfieldname": "qty_consumed_per_unit", "oldfieldtype": "Float", - "permlevel": 0, "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_27", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fieldname": "allow_alternative_item", "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Allow Alternative Item", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Allow Alternative Item" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, + "default": "0", "fetch_from": "item_code.include_item_in_manufacturing", "fieldname": "include_item_in_manufacturing", "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Include Item In Manufacturing", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Include Item In Manufacturing" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "original_item", "fieldtype": "Link", "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Original Item", - "length": 0, - "no_copy": 0, "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, "istable": 1, - "max_attachments": 0, - "modified": "2019-02-21 19:19:54.872459", + "modified": "2019-11-22 11:38:52.087303", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Item", "owner": "Administrator", "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py index 2ca4d16a07c..31a9fdb28ab 100644 --- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py +++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.py @@ -9,6 +9,7 @@ from frappe import _ from six import string_types from erpnext.manufacturing.doctype.bom.bom import get_boms_in_bottom_up_order from frappe.model.document import Document +import click class BOMUpdateTool(Document): def replace_bom(self): @@ -17,7 +18,8 @@ class BOMUpdateTool(Document): frappe.cache().delete_key('bom_children') bom_list = self.get_parent_boms(self.new_bom) updated_bom = [] - + with click.progressbar(bom_list) as bom_list: + pass for bom in bom_list: try: bom_obj = frappe.get_cached_doc('BOM', bom) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js index 95549d5a248..bc8c22998ae 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.js +++ b/erpnext/manufacturing/doctype/job_card/job_card.js @@ -3,6 +3,9 @@ frappe.ui.form.on('Job Card', { refresh: function(frm) { + frappe.flags.pause_job = 0; + frappe.flags.resume_job = 0; + if(!frm.doc.__islocal && frm.doc.items && frm.doc.items.length) { if (frm.doc.for_quantity != frm.doc.transferred_qty) { frm.add_custom_button(__("Material Request"), () => { @@ -13,44 +16,99 @@ frappe.ui.form.on('Job Card', { if (frm.doc.for_quantity != frm.doc.transferred_qty) { frm.add_custom_button(__("Material Transfer"), () => { frm.trigger("make_stock_entry"); - }); + }).addClass("btn-primary"); } } - if (frm.doc.docstatus == 0) { - frm.trigger("make_dashboard"); + if (frm.doc.docstatus == 0 && frm.doc.for_quantity > frm.doc.total_completed_qty + && (!frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty)) { + frm.trigger("prepare_timer_buttons"); + } + }, - if (!frm.doc.job_started) { - frm.add_custom_button(__("Start Job"), () => { - let row = frappe.model.add_child(frm.doc, 'Job Card Time Log', 'time_logs'); - row.from_time = frappe.datetime.now_datetime(); - frm.set_value('job_started', 1); - frm.set_value('started_time' , row.from_time); - frm.save(); - }); - } else { - frm.add_custom_button(__("Complete Job"), () => { - let completed_time = frappe.datetime.now_datetime(); - frm.doc.time_logs.forEach(d => { - if (d.from_time && !d.to_time) { - d.to_time = completed_time; - frm.set_value('started_time' , ''); - frm.set_value('job_started', 0); - frm.save(); + prepare_timer_buttons: function(frm) { + frm.trigger("make_dashboard"); + if (!frm.doc.job_started) { + frm.add_custom_button(__("Start"), () => { + if (!frm.doc.employee) { + frappe.prompt({fieldtype: 'Link', label: __('Employee'), options: "Employee", + fieldname: 'employee'}, d => { + if (d.employee) { + frm.set_value("employee", d.employee); } - }) - }); - } + + frm.events.start_job(frm); + }, __("Enter Value"), __("Start")); + } else { + frm.events.start_job(frm); + } + }).addClass("btn-primary"); + } else if (frm.doc.status == "On Hold") { + frm.add_custom_button(__("Resume"), () => { + frappe.flags.resume_job = 1; + frm.events.start_job(frm); + }).addClass("btn-primary"); + } else { + frm.add_custom_button(__("Pause"), () => { + frappe.flags.pause_job = 1; + frm.set_value("status", "On Hold"); + frm.events.complete_job(frm); + }); + + frm.add_custom_button(__("Complete"), () => { + let completed_time = frappe.datetime.now_datetime(); + frm.trigger("hide_timer"); + + frappe.prompt({fieldtype: 'Float', label: __('Completed Quantity'), + fieldname: 'qty', reqd: 1, default: frm.doc.for_quantity}, data => { + frm.events.complete_job(frm, completed_time, data.qty); + }, __("Enter Value"), __("Complete")); + }).addClass("btn-primary"); } }, + start_job: function(frm) { + let row = frappe.model.add_child(frm.doc, 'Job Card Time Log', 'time_logs'); + row.from_time = frappe.datetime.now_datetime(); + frm.set_value('job_started', 1); + frm.set_value('started_time' , row.from_time); + frm.set_value("status", "Work In Progress"); + + if (!frappe.flags.resume_job) { + frm.set_value('current_time' , 0); + } + + frm.save(); + }, + + complete_job: function(frm, completed_time, completed_qty) { + frm.doc.time_logs.forEach(d => { + if (d.from_time && !d.to_time) { + d.to_time = completed_time || frappe.datetime.now_datetime(); + d.completed_qty = completed_qty || 0; + + if(frappe.flags.pause_job) { + let currentIncrement = moment(d.to_time).diff(moment(d.from_time),"seconds") || 0; + frm.set_value('current_time' , currentIncrement + (frm.doc.current_time || 0)); + } else { + frm.set_value('started_time' , ''); + frm.set_value('job_started', 0); + frm.set_value('current_time' , 0); + } + + frm.save(); + } + }); + }, + make_dashboard: function(frm) { if(frm.doc.__islocal) return; frm.dashboard.refresh(); const timer = ` -
+
00 : 00 @@ -58,11 +116,16 @@ frappe.ui.form.on('Job Card', { 00
`; - var section = frm.dashboard.add_section(timer); + var section = frm.toolbar.page.add_inner_message(timer); - if (frm.doc.started_time) { - let currentIncrement = moment(frappe.datetime.now_datetime()).diff(moment(frm.doc.started_time),"seconds"); - initialiseTimer(); + let currentIncrement = frm.doc.current_time || 0; + if (frm.doc.started_time || frm.doc.current_time) { + if (frm.doc.status == "On Hold") { + updateStopwatch(currentIncrement); + } else { + currentIncrement += moment(frappe.datetime.now_datetime()).diff(moment(frm.doc.started_time),"seconds"); + initialiseTimer(); + } function initialiseTimer() { const interval = setInterval(function() { @@ -70,12 +133,12 @@ frappe.ui.form.on('Job Card', { updateStopwatch(current); }, 1000); } - + function updateStopwatch(increment) { var hours = Math.floor(increment / 3600); var minutes = Math.floor((increment - (hours * 3600)) / 60); var seconds = increment - (hours * 3600) - (minutes * 60); - + $(section).find(".hours").text(hours < 10 ? ("0" + hours.toString()) : hours.toString()); $(section).find(".minutes").text(minutes < 10 ? ("0" + minutes.toString()) : minutes.toString()); $(section).find(".seconds").text(seconds < 10 ? ("0" + seconds.toString()) : seconds.toString()); @@ -88,6 +151,10 @@ frappe.ui.form.on('Job Card', { } }, + hide_timer: function(frm) { + frm.toolbar.page.inner_toolbar.find(".stopwatch").remove(); + }, + for_quantity: function(frm) { frm.doc.items = []; frm.call({ @@ -117,5 +184,22 @@ frappe.ui.form.on('Job Card', { timer: function(frm) { return `` + }, + + set_total_completed_qty: function(frm) { + frm.doc.total_completed_qty = 0; + frm.doc.time_logs.forEach(d => { + if (d.completed_qty) { + frm.doc.total_completed_qty += d.completed_qty; + } + }); + + refresh_field("total_completed_qty"); } -}); \ No newline at end of file +}); + +frappe.ui.form.on('Job Card Time Log', { + completed_qty: function(frm) { + frm.events.set_total_completed_qty(frm); + } +}) \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/job_card/job_card.json b/erpnext/manufacturing/doctype/job_card/job_card.json index 39c5cce313b..156accee74e 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.json +++ b/erpnext/manufacturing/doctype/job_card/job_card.json @@ -1,1071 +1,350 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "PO-JOB.#####", - "beta": 0, - "creation": "2018-07-09 17:23:29.518745", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "autoname": "naming_series:", + "creation": "2018-07-09 17:23:29.518745", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "naming_series", + "work_order", + "bom_no", + "workstation", + "operation", + "column_break_4", + "posting_date", + "company", + "remarks", + "production_section", + "production_item", + "item_name", + "for_quantity", + "wip_warehouse", + "column_break_12", + "employee", + "employee_name", + "status", + "project", + "timing_detail", + "time_logs", + "section_break_13", + "total_completed_qty", + "total_time_in_mins", + "column_break_15", + "section_break_8", + "items", + "more_information", + "operation_id", + "transferred_qty", + "requested_qty", + "column_break_20", + "barcode", + "job_started", + "started_time", + "current_time", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "work_order", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Work Order", - "length": 0, - "no_copy": 0, - "options": "Work Order", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "work_order", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Work Order", + "options": "Work Order", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "bom_no", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "BOM No", - "length": 0, - "no_copy": 0, - "options": "BOM", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "work_order.bom_no", + "fieldname": "bom_no", + "fieldtype": "Link", + "label": "BOM No", + "options": "BOM", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "workstation", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Workstation", - "length": 0, - "no_copy": 0, - "options": "Workstation", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "workstation", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Workstation", + "options": "Workstation", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "operation", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Operation", - "length": 0, - "no_copy": 0, - "options": "Operation", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "operation", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Operation", + "options": "Operation", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fetch_if_empty": 0, - "fieldname": "posting_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Posting Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Posting Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "label": "Company", + "options": "Company", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "for_quantity", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "For Quantity", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "for_quantity", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Qty To Manufacture", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "wip_warehouse", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "WIP Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "wip_warehouse", + "fieldtype": "Link", + "label": "WIP Warehouse", + "options": "Warehouse", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "timing_detail", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Timing Detail", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "timing_detail", + "fieldtype": "Section Break", + "label": "Timing Detail" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "employee", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Employee", - "length": 0, - "no_copy": 0, - "options": "Employee", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "employee", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Employee", + "options": "Employee" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "time_logs", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Time Logs", - "length": 0, - "no_copy": 0, - "options": "Job Card Time Log", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "time_logs", + "fieldtype": "Table", + "label": "Time Logs", + "options": "Job Card Time Log" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_13", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_13", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "total_completed_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Completed Qty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_completed_qty", + "fieldtype": "Float", + "label": "Total Completed Qty", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_15", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_15", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "total_time_in_mins", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Time in Mins", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_time_in_mins", + "fieldtype": "Float", + "label": "Total Time in Mins", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "section_break_8", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Raw Materials", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_8", + "fieldtype": "Section Break", + "label": "Raw Materials" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "items", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Items", - "length": 0, - "no_copy": 0, - "options": "Job Card Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "items", + "fieldtype": "Table", + "label": "Items", + "options": "Job Card Item", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "more_information", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "More Information", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "more_information", + "fieldtype": "Section Break", + "label": "More Information" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "operation_id", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Operation ID", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "operation_id", + "fieldtype": "Data", + "hidden": 1, + "label": "Operation ID", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fetch_if_empty": 0, - "fieldname": "transferred_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transferred Qty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "transferred_qty", + "fieldtype": "Float", + "label": "Transferred Qty", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fetch_if_empty": 0, - "fieldname": "requested_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Requested Qty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "requested_qty", + "fieldtype": "Float", + "label": "Requested Qty", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "project", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Project", - "length": 0, - "no_copy": 0, - "options": "Project", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "remarks", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Remarks", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "remarks", + "fieldtype": "Small Text", + "label": "Remarks" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_20", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_20", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Open", - "fetch_if_empty": 0, - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 1, - "options": "Open\nWork In Progress\nMaterial Transferred\nSubmitted\nCancelled\nCompleted", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Open", + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "no_copy": 1, + "options": "Open\nWork In Progress\nMaterial Transferred\nOn Hold\nSubmitted\nCancelled\nCompleted", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "job_started", - "fieldtype": "Check", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Job Started", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "job_started", + "fieldtype": "Check", + "hidden": 1, + "label": "Job Started", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "started_time", - "fieldtype": "Datetime", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Started Time", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "started_time", + "fieldtype": "Datetime", + "hidden": 1, + "label": "Started Time", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Job Card", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Job Card", + "print_hide": 1, + "read_only": 1 + }, + { + "default": "PO-JOB.#####", + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Naming Series", + "options": "PO-JOB.#####", + "reqd": 1 + }, + { + "fetch_from": "employee.employee_name", + "fieldname": "employee_name", + "fieldtype": "Read Only", + "label": "Employee Name" + }, + { + "fieldname": "production_section", + "fieldtype": "Section Break", + "label": "Production" + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "fetch_from": "work_order.production_item", + "fieldname": "production_item", + "fieldtype": "Read Only", + "label": "Production Item" + }, + { + "fieldname": "barcode", + "fieldtype": "Barcode", + "label": "Barcode", + "read_only": 1 + }, + { + "fetch_from": "work_order.item_name", + "fieldname": "item_name", + "fieldtype": "Read Only", + "label": "Item Name" + }, + { + "fieldname": "current_time", + "fieldtype": "Int", + "hidden": 1, + "label": "Current Time", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-03-10 17:38:37.499871", - "modified_by": "Administrator", - "module": "Manufacturing", - "name": "Job Card", - "name_case": "", - "owner": "Administrator", + ], + "is_submittable": 1, + "modified": "2019-12-03 13:08:57.926201", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Job Card", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Manufacturing User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Manufacturing Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing Manager", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "operation", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "title_field": "operation", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index 9d2e620e584..029db1cb4e8 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -4,10 +4,16 @@ from __future__ import unicode_literals import frappe +import datetime from frappe import _ -from frappe.utils import flt, time_diff_in_hours, get_datetime from frappe.model.mapper import get_mapped_doc from frappe.model.document import Document +from frappe.utils import (flt, cint, time_diff_in_hours, get_datetime, getdate, + get_time, add_to_date, time_diff, add_days, get_datetime_str) + +from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations + +class OverlapError(frappe.ValidationError): pass class JobCard(Document): def validate(self): @@ -26,7 +32,7 @@ class JobCard(Document): data = self.get_overlap_for(d) if data: frappe.throw(_("Row {0}: From Time and To Time of {1} is overlapping with {2}") - .format(d.idx, self.name, data.name)) + .format(d.idx, self.name, data.name), OverlapError) if d.from_time and d.to_time: d.time_in_mins = time_diff_in_hours(d.to_time, d.from_time) * 60 @@ -35,27 +41,120 @@ class JobCard(Document): if d.completed_qty: self.total_completed_qty += d.completed_qty - def get_overlap_for(self, args): - existing = frappe.db.sql("""select jc.name as name from + def get_overlap_for(self, args, check_next_available_slot=False): + production_capacity = 1 + + if self.workstation: + production_capacity = frappe.get_cached_value("Workstation", + self.workstation, 'production_capacity') or 1 + validate_overlap_for = " and jc.workstation = %(workstation)s " + + if self.employee: + # override capacity for employee + production_capacity = 1 + validate_overlap_for = " and jc.employee = %(employee)s " + + extra_cond = '' + if check_next_available_slot: + extra_cond = " or (%(from_time)s <= jctl.from_time and %(to_time)s <= jctl.to_time)" + + existing = frappe.db.sql("""select jc.name as name, jctl.to_time from `tabJob Card Time Log` jctl, `tabJob Card` jc where jctl.parent = jc.name and ( (%(from_time)s > jctl.from_time and %(from_time)s < jctl.to_time) or (%(to_time)s > jctl.from_time and %(to_time)s < jctl.to_time) or - (%(from_time)s <= jctl.from_time and %(to_time)s >= jctl.to_time)) - and jctl.name!=%(name)s - and jc.name!=%(parent)s - and jc.docstatus < 2 - and jc.employee = %(employee)s """, + (%(from_time)s <= jctl.from_time and %(to_time)s >= jctl.to_time) {0} + ) + and jctl.name != %(name)s and jc.name != %(parent)s and jc.docstatus < 2 {1} + order by jctl.to_time desc limit 1""".format(extra_cond, validate_overlap_for), { "from_time": args.from_time, "to_time": args.to_time, "name": args.name or "No Name", "parent": args.parent or "No Name", - "employee": self.employee + "employee": self.employee, + "workstation": self.workstation }, as_dict=True) + if existing and production_capacity > len(existing): + return + return existing[0] if existing else None + def schedule_time_logs(self, row): + row.remaining_time_in_mins = row.time_in_mins + while row.remaining_time_in_mins > 0: + args = frappe._dict({ + "from_time": row.planned_start_time, + "to_time": row.planned_end_time + }) + + self.validate_overlap_for_workstation(args, row) + self.check_workstation_time(row) + + def validate_overlap_for_workstation(self, args, row): + # get the last record based on the to time from the job card + data = self.get_overlap_for(args, check_next_available_slot=True) + if data: + row.planned_start_time = get_datetime(data.to_time + get_mins_between_operations()) + + def check_workstation_time(self, row): + workstation_doc = frappe.get_cached_doc("Workstation", self.workstation) + if (not workstation_doc.working_hours or + cint(frappe.db.get_single_value("Manufacturing Settings", "allow_overtime"))): + row.remaining_time_in_mins -= time_diff_in_minutes(row.planned_end_time, + row.planned_start_time) + + self.update_time_logs(row) + return + + start_date = getdate(row.planned_start_time) + start_time = get_time(row.planned_start_time) + + new_start_date = workstation_doc.validate_workstation_holiday(start_date) + + if new_start_date != start_date: + row.planned_start_time = datetime.datetime.combine(new_start_date, start_time) + start_date = new_start_date + + total_idx = len(workstation_doc.working_hours) + + for i, time_slot in enumerate(workstation_doc.working_hours): + workstation_start_time = datetime.datetime.combine(start_date, get_time(time_slot.start_time)) + workstation_end_time = datetime.datetime.combine(start_date, get_time(time_slot.end_time)) + + if (get_datetime(row.planned_start_time) >= workstation_start_time and + get_datetime(row.planned_start_time) <= workstation_end_time): + time_in_mins = time_diff_in_minutes(workstation_end_time, row.planned_start_time) + + # If remaining time fit in workstation time logs else split hours as per workstation time + if time_in_mins > row.remaining_time_in_mins: + row.planned_end_time = add_to_date(row.planned_start_time, + minutes=row.remaining_time_in_mins) + row.remaining_time_in_mins = 0 + else: + row.planned_end_time = add_to_date(row.planned_start_time, minutes=time_in_mins) + row.remaining_time_in_mins -= time_in_mins + + self.update_time_logs(row) + + if total_idx != (i+1) and row.remaining_time_in_mins > 0: + row.planned_start_time = datetime.datetime.combine(start_date, + get_time(workstation_doc.working_hours[i+1].start_time)) + + if row.remaining_time_in_mins > 0: + start_date = add_days(start_date, 1) + row.planned_start_time = datetime.datetime.combine(start_date, + get_time(workstation_doc.working_hours[0].start_time)) + + def update_time_logs(self, row): + self.append("time_logs", { + "from_time": row.planned_start_time, + "to_time": row.planned_end_time, + "completed_qty": 0, + "time_in_mins": time_diff_in_minutes(row.planned_end_time, row.planned_start_time), + }) + def get_required_items(self): if not self.get('work_order'): return @@ -95,8 +194,9 @@ class JobCard(Document): if self.total_completed_qty <= 0.0: frappe.throw(_("Total completed qty must be greater than zero")) - if self.total_completed_qty > self.for_quantity: - frappe.throw(_("Total completed qty can not be greater than for quantity")) + if self.total_completed_qty != self.for_quantity: + frappe.throw(_("The total completed qty({0}) must be equal to qty to manufacture({1})") + .format(frappe.bold(self.total_completed_qty),frappe.bold(self.for_quantity))) def update_work_order(self): if not self.work_order: @@ -172,6 +272,8 @@ class JobCard(Document): self.set_status(update_status) def set_status(self, update_status=False): + if self.status == "On Hold": return + self.status = { 0: "Open", 1: "Submitted", @@ -230,6 +332,7 @@ def make_stock_entry(source_name, target_doc=None): target.fg_completed_qty = source.get('for_quantity', 0) - source.get('transferred_qty', 0) target.calculate_rate_and_amount() target.set_missing_values() + target.set_stock_entry_type() doclist = get_mapped_doc("Job Card", source_name, { "Job Card": { @@ -251,3 +354,48 @@ def make_stock_entry(source_name, target_doc=None): }, target_doc, set_missing_values) return doclist + +def time_diff_in_minutes(string_ed_date, string_st_date): + return time_diff(string_ed_date, string_st_date).total_seconds() / 60 + +@frappe.whitelist() +def get_job_details(start, end, filters=None): + events = [] + + event_color = { + "Completed": "#cdf5a6", + "Material Transferred": "#ffdd9e", + "Work In Progress": "#D3D3D3" + } + + from frappe.desk.reportview import get_filters_cond + conditions = get_filters_cond("Job Card", filters, []) + + job_cards = frappe.db.sql(""" SELECT `tabJob Card`.name, `tabJob Card`.work_order, + `tabJob Card`.employee_name, `tabJob Card`.status, ifnull(`tabJob Card`.remarks, ''), + min(`tabJob Card Time Log`.from_time) as from_time, + max(`tabJob Card Time Log`.to_time) as to_time + FROM `tabJob Card` , `tabJob Card Time Log` + WHERE + `tabJob Card`.name = `tabJob Card Time Log`.parent {0} + group by `tabJob Card`.name""".format(conditions), as_dict=1) + + for d in job_cards: + subject_data = [] + for field in ["name", "work_order", "remarks", "employee_name"]: + if not d.get(field): continue + + subject_data.append(d.get(field)) + + color = event_color.get(d.status) + job_card_data = { + 'from_time': d.from_time, + 'to_time': d.to_time, + 'name': d.name, + 'subject': '\n'.join(subject_data), + 'color': color if color else "#89bcde" + } + + events.append(job_card_data) + + return events diff --git a/erpnext/manufacturing/doctype/job_card/job_card_calendar.js b/erpnext/manufacturing/doctype/job_card/job_card_calendar.js new file mode 100644 index 00000000000..cf07698ad6a --- /dev/null +++ b/erpnext/manufacturing/doctype/job_card/job_card_calendar.js @@ -0,0 +1,21 @@ +frappe.views.calendar["Job Card"] = { + field_map: { + "start": "from_time", + "end": "to_time", + "id": "name", + "title": "subject", + "color": "color", + "allDay": "allDay", + "progress": "progress" + }, + gantt: true, + filters: [ + { + "fieldtype": "Link", + "fieldname": "employee", + "options": "Employee", + "label": __("Employee") + } + ], + get_events_method: "erpnext.manufacturing.doctype.job_card.job_card.get_job_details" +}; diff --git a/erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.json b/erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.json index 2aab71dee4f..9dd54dd6182 100644 --- a/erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.json +++ b/erpnext/manufacturing/doctype/job_card_time_log/job_card_time_log.json @@ -1,208 +1,57 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2019-03-08 23:56:43.187569", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "creation": "2019-03-08 23:56:43.187569", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "from_time", + "to_time", + "column_break_2", + "time_in_mins", + "completed_qty" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "from_time", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "From Time", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "from_time", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "From Time" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "to_time", - "fieldtype": "Datetime", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "To Time", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "to_time", + "fieldtype": "Datetime", + "in_list_view": 1, + "label": "To Time" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "time_in_mins", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Time In Mins", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "time_in_mins", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Time In Mins", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fetch_if_empty": 0, - "fieldname": "completed_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Completed Qty", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "0", + "fieldname": "completed_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Completed Qty", + "reqd": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-03-10 17:08:46.504910", - "modified_by": "Administrator", - "module": "Manufacturing", - "name": "Job Card Time Log", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "ASC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "istable": 1, + "modified": "2019-12-03 12:56:02.285448", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Job Card Time Log", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "ASC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json index 461b9ab3dfa..86fa7a8901a 100644 --- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json +++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json @@ -1,585 +1,178 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2014-11-27 14:12:07.542534", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 0, - "engine": "InnoDB", + "creation": "2014-11-27 14:12:07.542534", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "raw_materials_consumption_section", + "material_consumption", + "column_break_3", + "backflush_raw_materials_based_on", + "capacity_planning", + "disable_capacity_planning", + "allow_overtime", + "allow_production_on_holidays", + "column_break_5", + "capacity_planning_for_days", + "mins_between_operations", + "section_break_6", + "default_wip_warehouse", + "default_fg_warehouse", + "column_break_11", + "default_scrap_warehouse", + "over_production_for_sales_and_work_order_section", + "overproduction_percentage_for_sales_order", + "column_break_16", + "overproduction_percentage_for_work_order", + "other_settings_section", + "update_bom_costs_automatically" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "capacity_planning", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Capacity Planning", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "capacity_planning", + "fieldtype": "Section Break", + "label": "Capacity Planning" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Disables creation of time logs against Work Orders. Operations shall not be tracked against Work Order", - "fieldname": "disable_capacity_planning", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Disable Capacity Planning and Time Tracking", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "depends_on": "eval:!doc.disable_capacity_planning", + "description": "Plan time logs outside Workstation Working Hours.", + "fieldname": "allow_overtime", + "fieldtype": "Check", + "label": "Allow Overtime" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Plan time logs outside Workstation Working Hours.", - "fieldname": "allow_overtime", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Allow Overtime", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "depends_on": "eval:!doc.disable_capacity_planning", + "fieldname": "allow_production_on_holidays", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Allow Production on Holidays" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "allow_production_on_holidays", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Allow Production on Holidays", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "30", + "depends_on": "eval:!doc.disable_capacity_planning", + "description": "Try planning operations for X days in advance.", + "fieldname": "capacity_planning_for_days", + "fieldtype": "Int", + "label": "Capacity Planning For (Days)" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "30", - "description": "Try planning operations for X days in advance.", - "fieldname": "capacity_planning_for_days", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Capacity Planning For (Days)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:!doc.disable_capacity_planning", + "description": "Default 10 mins", + "fieldname": "mins_between_operations", + "fieldtype": "Int", + "label": "Time Between Operations (in mins)" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Default 10 mins", - "fieldname": "mins_between_operations", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Time Between Operations (in mins)", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_6", + "fieldtype": "Section Break", + "label": "Default Warehouses for Production" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "overproduction_percentage_for_sales_order", + "fieldtype": "Percent", + "label": "Overproduction Percentage For Sales Order" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "overproduction_percentage_for_sales_order", - "fieldtype": "Percent", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Overproduction Percentage For Sales Order", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "overproduction_percentage_for_work_order", + "fieldtype": "Percent", + "label": "Overproduction Percentage For Work Order" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "overproduction_percentage_for_work_order", - "fieldtype": "Percent", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Overproduction Percentage For Work Order", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "BOM", + "fieldname": "backflush_raw_materials_based_on", + "fieldtype": "Select", + "label": "Backflush Raw Materials Based On", + "options": "BOM\nMaterial Transferred for Manufacture" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "BOM", - "fieldname": "backflush_raw_materials_based_on", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Backflush Raw Materials Based On", - "length": 0, - "no_copy": 0, - "options": "BOM\nMaterial Transferred for Manufacture", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "description": "Allow multiple Material Consumption against a Work Order", + "fieldname": "material_consumption", + "fieldtype": "Check", + "label": "Allow Multiple Material Consumption" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Allow multiple Material Consumption against a Work Order", - "fieldname": "material_consumption", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Allow Multiple Material Consumption", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "description": "Update BOM cost automatically via Scheduler, based on latest valuation rate / price list rate / last purchase rate of raw materials.", + "fieldname": "update_bom_costs_automatically", + "fieldtype": "Check", + "label": "Update BOM Cost Automatically" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Update BOM cost automatically via Scheduler, based on latest valuation rate / price list rate / last purchase rate of raw materials.", - "fieldname": "update_bom_costs_automatically", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Update BOM Cost Automatically", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_11", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "default_wip_warehouse", + "fieldtype": "Link", + "label": "Default Work In Progress Warehouse", + "options": "Warehouse" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "default_wip_warehouse", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Default Work In Progress Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "default_fg_warehouse", + "fieldtype": "Link", + "label": "Default Finished Goods Warehouse", + "options": "Warehouse" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "default_fg_warehouse", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Default Finished Goods Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "0", + "fieldname": "disable_capacity_planning", + "fieldtype": "Check", + "label": "Disable Capacity Planning" + }, + { + "fieldname": "default_scrap_warehouse", + "fieldtype": "Link", + "label": "Default Scrap Warehouse", + "options": "Warehouse" + }, + { + "fieldname": "over_production_for_sales_and_work_order_section", + "fieldtype": "Section Break", + "label": "Over Production for Sales and Work Order" + }, + { + "fieldname": "raw_materials_consumption_section", + "fieldtype": "Section Break", + "label": "Raw Materials Consumption" + }, + { + "fieldname": "column_break_16", + "fieldtype": "Column Break" + }, + { + "fieldname": "other_settings_section", + "fieldtype": "Section Break", + "label": "Other Settings" + }, + { + "fieldname": "column_break_5", + "fieldtype": "Column Break" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "icon-wrench", - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "menu_index": 0, - "modified": "2018-05-28 00:46:25.310621", - "modified_by": "Administrator", - "module": "Manufacturing", - "name": "Manufacturing Settings", - "name_case": "", - "owner": "Administrator", + ], + "icon": "icon-wrench", + "issingle": 1, + "modified": "2019-11-26 13:10:45.569341", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Manufacturing Settings", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "Manufacturing Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "read": 1, + "role": "Manufacturing Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index 51989378d8f..2b168d1d76d 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -3,6 +3,11 @@ frappe.ui.form.on('Production Plan', { setup: function(frm) { + frm.custom_make_buttons = { + 'Work Order': 'Work Order', + 'Material Request': 'Material Request', + }; + frm.fields_dict['po_items'].grid.get_field('warehouse').get_query = function(doc) { return { filters: { @@ -66,12 +71,13 @@ frappe.ui.form.on('Production Plan', { }, __('Create')); } + frm.page.set_inner_btn_group_as_primary(__('Create')); frm.trigger("material_requirement"); const projected_qty_formula = `
-

+

${__("Projected Quantity Formula")} diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json index 0be9f1a8eef..77ca6b63d3f 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.json +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json @@ -1,1391 +1,322 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2017-10-29 11:53:09.523362", - "custom": 0, - "default_print_format": "", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 0, - "engine": "InnoDB", + "actions": [], + "autoname": "naming_series:", + "creation": "2017-10-29 11:53:09.523362", + "doctype": "DocType", + "document_type": "Document", + "engine": "InnoDB", + "field_order": [ + "naming_series", + "company", + "get_items_from", + "column_break1", + "posting_date", + "filters", + "item_code", + "customer", + "warehouse", + "project", + "column_break2", + "from_date", + "to_date", + "sales_orders_detail", + "get_sales_orders", + "sales_orders", + "material_request_detail", + "get_material_request", + "material_requests", + "select_items_to_manufacture_section", + "get_items", + "po_items", + "material_request_planning", + "include_non_stock_items", + "include_subcontracted_items", + "ignore_existing_ordered_qty", + "column_break_25", + "for_warehouse", + "download_materials_required", + "get_items_for_mr", + "section_break_27", + "mr_items", + "other_details", + "total_planned_qty", + "total_produced_qty", + "column_break_32", + "status", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fetch_if_empty": 0, - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Naming Series", - "length": 0, - "no_copy": 0, - "options": "MFG-PP-.YYYY.-", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Naming Series", + "options": "MFG-PP-.YYYY.-", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fetch_if_empty": 0, - "fieldname": "get_items_from", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Get Items From", - "length": 0, - "no_copy": 0, - "options": "\nSales Order\nMaterial Request", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "get_items_from", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Get Items From", + "options": "\nSales Order\nMaterial Request" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break1", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break1", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "fetch_if_empty": 0, - "fieldname": "posting_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Posting Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Posting Date", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "eval: doc.__islocal", - "columns": 0, - "depends_on": "eval: doc.get_items_from", - "description": "", - "fetch_if_empty": 0, - "fieldname": "filters", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Filters", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "collapsible_depends_on": "eval: doc.__islocal", + "depends_on": "eval: doc.get_items_from", + "fieldname": "filters", + "fieldtype": "Section Break", + "label": "Filters" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "item_code", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Item Code", - "length": 0, - "no_copy": 0, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Code", + "options": "Item" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: doc.get_items_from == \"Sales Order\"", - "fetch_if_empty": 0, - "fieldname": "customer", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Customer", - "length": 0, - "no_copy": 0, - "options": "Customer", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval: doc.get_items_from == \"Sales Order\"", + "fieldname": "customer", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Customer", + "options": "Customer" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: doc.get_items_from == \"Material Request\"", - "fetch_if_empty": 0, - "fieldname": "warehouse", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval: doc.get_items_from == \"Material Request\"", + "fieldname": "warehouse", + "fieldtype": "Link", + "label": "Warehouse", + "options": "Warehouse" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval: doc.get_items_from == \"Sales Order\"", - "fetch_if_empty": 0, - "fieldname": "project", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Project", - "length": 0, - "no_copy": 0, - "options": "Project", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval: doc.get_items_from == \"Sales Order\"", + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "options": "Project" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "column_break2", + "fieldtype": "Column Break", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "from_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "From Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "from_date", + "fieldtype": "Date", + "label": "From Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "to_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "To Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "to_date", + "fieldtype": "Date", + "label": "To Date" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "eval: doc.__islocal", - "columns": 0, - "depends_on": "eval: doc.get_items_from == \"Sales Order\"", - "fetch_if_empty": 0, - "fieldname": "sales_orders_detail", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sales Orders Detail", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "collapsible_depends_on": "eval: doc.__islocal", + "depends_on": "eval: doc.get_items_from == \"Sales Order\"", + "fieldname": "sales_orders_detail", + "fieldtype": "Section Break", + "label": "Sales Orders" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fetch_if_empty": 0, - "fieldname": "get_sales_orders", - "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Get Sales Orders", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "get_sales_orders", + "fieldtype": "Button", + "label": "Get Sales Orders" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "sales_orders", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sales Orders", - "length": 0, - "no_copy": 1, - "options": "Production Plan Sales Order", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sales_orders", + "fieldtype": "Table", + "label": "Sales Orders", + "no_copy": 1, + "options": "Production Plan Sales Order" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "eval: doc.__islocal", - "columns": 0, - "depends_on": "eval: doc.get_items_from == \"Material Request\"", - "fetch_if_empty": 0, - "fieldname": "material_request_detail", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Material Request Detail", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "collapsible_depends_on": "eval: doc.__islocal", + "depends_on": "eval: doc.get_items_from == \"Material Request\"", + "fieldname": "material_request_detail", + "fieldtype": "Section Break", + "label": "Material Request Detail" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fetch_if_empty": 0, - "fieldname": "get_material_request", - "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Get Material Request", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "get_material_request", + "fieldtype": "Button", + "label": "Get Material Request" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "material_requests", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Material Requests", - "length": 0, - "no_copy": 1, - "options": "Production Plan Material Request", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "material_requests", + "fieldtype": "Table", + "label": "Material Requests", + "no_copy": 1, + "options": "Production Plan Material Request" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fetch_if_empty": 0, - "fieldname": "select_items_to_manufacture_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Select Items to Manufacture", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "select_items_to_manufacture_section", + "fieldtype": "Section Break", + "label": "Select Items to Manufacture" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "get_items_from", - "fetch_if_empty": 0, - "fieldname": "get_items", - "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Get Items For Work Order", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "get_items_from", + "fieldname": "get_items", + "fieldtype": "Button", + "label": "Get Items For Work Order" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "po_items", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 1, - "options": "Production Plan Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "po_items", + "fieldtype": "Table", + "no_copy": 1, + "options": "Production Plan Item", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, - "fieldname": "material_request_planning", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Material Request Planning", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "material_request_planning", + "fieldtype": "Section Break", + "label": "Material Request Planning" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "fetch_if_empty": 0, - "fieldname": "include_non_stock_items", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Include Non Stock Items", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "1", + "fieldname": "include_non_stock_items", + "fieldtype": "Check", + "label": "Include Non Stock Items" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "1", - "fetch_if_empty": 0, - "fieldname": "include_subcontracted_items", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Include Subcontracted Items", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "1", + "fieldname": "include_subcontracted_items", + "fieldtype": "Check", + "label": "Include Subcontracted Items" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "If enabled, then system will create the material even if the raw materials are available", - "fetch_if_empty": 0, - "fieldname": "ignore_existing_ordered_qty", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Ignore Existing Projected Quantity", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "description": "To know more about projected quantity, click here.", + "fieldname": "ignore_existing_ordered_qty", + "fieldtype": "Check", + "label": "Ignore Existing Projected Quantity" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_25", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_25", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "for_warehouse", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "For Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "for_warehouse", + "fieldtype": "Link", + "label": "For Warehouse", + "options": "Warehouse" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "download_materials_required", - "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Download Materials Required", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:!doc.__islocal", + "fieldname": "download_materials_required", + "fieldtype": "Button", + "label": "Download Required Materials" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "get_items_for_mr", - "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Get Raw Materials For Production", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "get_items_for_mr", + "fieldtype": "Button", + "label": "Get Raw Materials For Production" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fetch_if_empty": 0, - "fieldname": "section_break_27", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_27", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "mr_items", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Material Request Plan Item", - "length": 0, - "no_copy": 1, - "options": "Material Request Plan Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "mr_items", + "fieldtype": "Table", + "label": "Material Request Plan Item", + "no_copy": 1, + "options": "Material Request Plan Item" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fetch_if_empty": 0, - "fieldname": "projected_qty_formula", - "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Projected Qty Formula", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "other_details", + "fieldtype": "Section Break", + "label": "Other Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "other_details", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Other Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "total_planned_qty", + "fieldtype": "Float", + "label": "Total Planned Qty", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fetch_if_empty": 0, - "fieldname": "total_planned_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Planned Qty", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "total_produced_qty", + "fieldtype": "Float", + "label": "Total Produced Qty", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0", - "fetch_if_empty": 0, - "fieldname": "total_produced_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Produced Qty", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_32", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "column_break_32", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Draft", + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "no_copy": 1, + "options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nStopped\nCancelled\nMaterial Requested", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Draft", - "fetch_if_empty": 0, - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 1, - "options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nStopped\nCancelled\nMaterial Requested", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_if_empty": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "options": "Production Plan", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Production Plan", + "print_hide": 1, + "read_only": 1 } - ], - "has_web_view": 0, - "hide_toolbar": 0, - "icon": "fa fa-calendar", - "idx": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-04-09 12:05:14.300886", - "modified_by": "Administrator", - "module": "Manufacturing", - "name": "Production Plan", - "name_case": "", - "owner": "Administrator", + ], + "icon": "fa fa-calendar", + "is_submittable": 1, + "links": [], + "modified": "2020-01-21 19:13:10.113854", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Production Plan", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "Manufacturing User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "Manufacturing User", + "share": 1, + "submit": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "ASC", - "title_field": "", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "sort_field": "modified", + "sort_order": "ASC" } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 0f49f73dfb9..848c53c8046 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -22,7 +22,7 @@ class ProductionPlan(Document): def validate_data(self): for d in self.get('po_items'): if not d.bom_no: - frappe.throw(_("Please select BOM for Item in Row {0}".format(d.idx))) + frappe.throw(_("Please select BOM for Item in Row {0}").format(d.idx)) else: validate_bom_no(d.item_code, d.bom_no) @@ -99,7 +99,7 @@ class ProductionPlan(Document): self.get_mr_items() def get_so_items(self): - so_list = [d.sales_order for d in self.get("sales_orders", []) if d.sales_order] + so_list = [d.sales_order for d in self.sales_orders if d.sales_order] if not so_list: msgprint(_("Please enter Sales Orders in the above table")) return [] @@ -109,7 +109,7 @@ class ProductionPlan(Document): item_condition = ' and so_item.item_code = {0}'.format(frappe.db.escape(self.item_code)) items = frappe.db.sql("""select distinct parent, item_code, warehouse, - (qty - work_order_qty) * conversion_factor as pending_qty, name + (qty - work_order_qty) * conversion_factor as pending_qty, description, name from `tabSales Order Item` so_item where parent in (%s) and docstatus = 1 and qty > work_order_qty and exists (select name from `tabBOM` bom where bom.item=so_item.item_code @@ -121,7 +121,7 @@ class ProductionPlan(Document): packed_items = frappe.db.sql("""select distinct pi.parent, pi.item_code, pi.warehouse as warehouse, (((so_item.qty - so_item.work_order_qty) * pi.qty) / so_item.qty) - as pending_qty, pi.parent_item, so_item.name + as pending_qty, pi.parent_item, pi.description, so_item.name from `tabSales Order Item` so_item, `tabPacked Item` pi where so_item.parent = pi.parent and so_item.docstatus = 1 and pi.parent_item = so_item.item_code @@ -134,7 +134,7 @@ class ProductionPlan(Document): self.calculate_total_planned_qty() def get_mr_items(self): - mr_list = [d.material_request for d in self.get("material_requests", []) if d.material_request] + mr_list = [d.material_request for d in self.material_requests if d.material_request] if not mr_list: msgprint(_("Please enter Material Requests in the above table")) return [] @@ -143,7 +143,7 @@ class ProductionPlan(Document): if self.item_code: item_condition = " and mr_item.item_code ={0}".format(frappe.db.escape(self.item_code)) - items = frappe.db.sql("""select distinct parent, name, item_code, warehouse, + items = frappe.db.sql("""select distinct parent, name, item_code, warehouse, description, (qty - ordered_qty) as pending_qty from `tabMaterial Request Item` mr_item where parent in (%s) and docstatus = 1 and qty > ordered_qty @@ -162,7 +162,7 @@ class ProductionPlan(Document): 'include_exploded_items': 1, 'warehouse': data.warehouse, 'item_code': data.item_code, - 'description': item_details and item_details.description or '', + 'description': data.description or item_details.description, 'stock_uom': item_details and item_details.stock_uom or '', 'bom_no': item_details and item_details.bom_no or '', 'planned_qty': data.pending_qty, @@ -174,10 +174,12 @@ class ProductionPlan(Document): if self.get_items_from == "Sales Order": pi.sales_order = data.parent pi.sales_order_item = data.name + pi.description = data.description elif self.get_items_from == "Material Request": pi.material_request = data.parent pi.material_request_item = data.name + pi.description = data.description def calculate_total_planned_qty(self): self.total_planned_qty = 0 @@ -195,7 +197,6 @@ class ProductionPlan(Document): for data in self.po_items: if data.name == production_plan_item: data.produced_qty = produced_qty - data.pending_qty = data.planned_qty - data.produced_qty data.db_update() self.calculate_total_produced_qty() @@ -302,6 +303,7 @@ class ProductionPlan(Document): wo_list.extend(work_orders) frappe.flags.mute_messages = False + if wo_list: wo_list = ["""%s""" % \ (p, p) for p in wo_list] @@ -309,16 +311,15 @@ class ProductionPlan(Document): else : msgprint(_("No Work Orders created")) - def make_work_order_for_sub_assembly_items(self, item): work_orders = [] bom_data = {} - get_sub_assembly_items(item.get("bom_no"), bom_data, item.get("qty")) + get_sub_assembly_items(item.get("bom_no"), bom_data) for key, data in bom_data.items(): data.update({ - 'qty': data.get("stock_qty"), + 'qty': data.get("stock_qty") * item.get("qty"), 'production_plan': self.name, 'company': self.company, 'fg_warehouse': item.get("fg_warehouse"), @@ -528,7 +529,6 @@ def get_material_request_items(row, sales_order, required_qty = ceil(required_qty) if required_qty > 0: - print(row) return { 'item_code': row.item_code, 'item_name': row.item_name, @@ -561,7 +561,7 @@ def get_sales_orders(self): item_filter += " and so_item.item_code = %(item)s" open_so = frappe.db.sql(""" - select distinct so.name, so.transaction_date, so.customer, so.base_grand_total as grand_total + select distinct so.name, so.transaction_date, so.customer, so.base_grand_total from `tabSales Order` so, `tabSales Order Item` so_item where so_item.parent = so.name and so.docstatus = 1 and so.status not in ("Stopped", "Closed") @@ -615,6 +615,9 @@ def get_items_for_material_requests(doc, ignore_existing_ordered_qty=None): doc['mr_items'] = [] po_items = doc.get('po_items') if doc.get('po_items') else doc.get('items') + if not po_items: + frappe.throw(_("Items are required to pull the raw materials which is associated with it.")) + company = doc.get('company') warehouse = doc.get('for_warehouse') @@ -625,7 +628,7 @@ def get_items_for_material_requests(doc, ignore_existing_ordered_qty=None): for data in po_items: planned_qty = data.get('required_qty') or data.get('planned_qty') ignore_existing_ordered_qty = data.get('ignore_existing_ordered_qty') or ignore_existing_ordered_qty - warehouse = warehouse or data.get("warehouse") + warehouse = data.get("warehouse") or warehouse item_details = {} if data.get("bom") or data.get("bom_no"): @@ -708,11 +711,11 @@ def get_item_data(item_code): return { "bom_no": item_details.get("bom_no"), - "stock_uom": item_details.get("stock_uom"), - "description": item_details.get("description") + "stock_uom": item_details.get("stock_uom") +# "description": item_details.get("description") } -def get_sub_assembly_items(bom_no, bom_data, qty): +def get_sub_assembly_items(bom_no, bom_data): data = get_children('BOM', parent = bom_no) for d in data: if d.expandable: @@ -729,6 +732,6 @@ def get_sub_assembly_items(bom_no, bom_data, qty): }) bom_item = bom_data.get(key) - bom_item["stock_qty"] += ((d.stock_qty * qty) / d.parent_bom_qty) + bom_item["stock_qty"] += d.stock_qty / d.parent_bom_qty - get_sub_assembly_items(bom_item.get("bom_no"), bom_data, bom_item["stock_qty"]) + get_sub_assembly_items(bom_item.get("bom_no"), bom_data) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py b/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py index 91c28555d61..09ec24a67a2 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan_dashboard.py @@ -6,7 +6,7 @@ def get_data(): 'fieldname': 'production_plan', 'transactions': [ { - 'label': _('Related'), + 'label': _('Transactions'), 'items': ['Work Order', 'Material Request'] }, ] diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 44796417d42..f70c9cc43fc 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -11,11 +11,9 @@ from erpnext.manufacturing.doctype.production_plan.production_plan import get_sa from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order from erpnext.manufacturing.doctype.production_plan.production_plan import get_items_for_material_requests -from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory class TestProductionPlan(unittest.TestCase): def setUp(self): - set_perpetual_inventory(0) for item in ['Test Production Item 1', 'Subassembly Item 1', 'Raw Material Item 1', 'Raw Material Item 2']: create_item(item, valuation_rate=100) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index ea2e7a96e1d..0a8f41fc49f 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -5,10 +5,10 @@ from __future__ import unicode_literals import unittest import frappe -from frappe.utils import flt, time_diff_in_hours, now, add_days, cint +from frappe.utils import flt, time_diff_in_hours, now, add_months, cint, today from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory -from erpnext.manufacturing.doctype.work_order.work_order \ - import make_stock_entry, ItemHasVariantError, stop_unstop, StockOverProductionError, OverProductionError +from erpnext.manufacturing.doctype.work_order.work_order import (make_stock_entry, + ItemHasVariantError, stop_unstop, StockOverProductionError, OverProductionError, CapacityError) from erpnext.stock.doctype.stock_entry import test_stock_entry from erpnext.stock.utils import get_bin from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order @@ -307,14 +307,50 @@ class TestWorkOrder(unittest.TestCase): {'docstatus': 1, 'with_operations': 1, 'company': '_Test Company'}, ['name', 'item']) if data: + frappe.db.set_value("Manufacturing Settings", + None, "disable_capacity_planning", 0) + bom, bom_item = data bom_doc = frappe.get_doc('BOM', bom) work_order = make_wo_order_test_record(item=bom_item, qty=1, bom_no=bom) + self.assertTrue(work_order.planned_end_date) job_cards = frappe.get_all('Job Card', filters = {'work_order': work_order.name}) self.assertEqual(len(job_cards), len(bom_doc.operations)) + def test_capcity_planning(self): + frappe.db.set_value("Manufacturing Settings", None, { + "disable_capacity_planning": 0, + "capacity_planning_for_days": 1 + }) + + data = frappe.get_cached_value('BOM', {'docstatus': 1, 'item': '_Test FG Item 2', + 'with_operations': 1, 'company': '_Test Company'}, ['name', 'item']) + + if data: + bom, bom_item = data + + planned_start_date = add_months(today(), months=-1) + work_order = make_wo_order_test_record(item=bom_item, + qty=10, bom_no=bom, planned_start_date=planned_start_date) + + work_order1 = make_wo_order_test_record(item=bom_item, + qty=30, bom_no=bom, planned_start_date=planned_start_date, do_not_submit=1) + + self.assertRaises(CapacityError, work_order1.submit) + + frappe.db.set_value("Manufacturing Settings", None, { + "capacity_planning_for_days": 30 + }) + + work_order1.reload() + work_order1.submit() + self.assertTrue(work_order1.docstatus, 1) + + work_order1.cancel() + work_order.cancel() + def test_work_order_with_non_transfer_item(self): items = {'Finished Good Transfer Item': 1, '_Test FG Item': 1, '_Test FG Item 1': 0} for item, allow_transfer in items.items(): @@ -371,14 +407,12 @@ def make_wo_order_test_record(**args): wo_order.skip_transfer=1 wo_order.get_items_and_operations_from_bom() wo_order.sales_order = args.sales_order or None + wo_order.planned_start_date = args.planned_start_date or now() if args.source_warehouse: for item in wo_order.get("required_items"): item.source_warehouse = args.source_warehouse - if args.planned_start_date: - wo_order.planned_start_date = args.planned_start_date - if not args.do_not_save: wo_order.insert() diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index cdbce33e1fd..9c8aa453a68 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -6,6 +6,7 @@ frappe.ui.form.on("Work Order", { frm.custom_make_buttons = { 'Stock Entry': 'Start', 'Pick List': 'Create Pick List', + 'Job Card': 'Create Job Card' }; // Set query for warehouses @@ -76,8 +77,7 @@ frappe.ui.form.on("Work Order", { return { query: "erpnext.controllers.queries.item_query", filters:[ - ['is_stock_item', '=',1], - ['default_bom', '!=', ''] + ['is_stock_item', '=',1] ] }; }); @@ -121,6 +121,15 @@ frappe.ui.form.on("Work Order", { } }, + source_warehouse: function(frm) { + if (frm.doc.source_warehouse) { + frm.doc.required_items.forEach(d => { + frappe.model.set_value(d.doctype, d.name, + "source_warehouse", frm.doc.source_warehouse); + }); + } + }, + refresh: function(frm) { erpnext.toggle_naming_series(); erpnext.work_order.set_custom_buttons(frm); @@ -131,7 +140,8 @@ frappe.ui.form.on("Work Order", { } if (frm.doc.docstatus===1) { - frm.trigger('show_progress'); + frm.trigger('show_progress_for_items'); + frm.trigger('show_progress_for_operations'); } if (frm.doc.docstatus === 1 @@ -179,89 +189,72 @@ frappe.ui.form.on("Work Order", { make_job_card: function(frm) { let qty = 0; - const fields = [{ - fieldtype: "Link", - fieldname: "operation", - options: "Operation", - label: __("Operation"), - get_query: () => { - const filter_workstation = frm.doc.operations.filter(d => { - if (d.status != "Completed") { - return d; - } - }); + let operations_data = []; - return { - filters: { - name: ["in", (filter_workstation || []).map(d => d.operation)] - } - }; - }, - reqd: true - }, { - fieldtype: "Link", - fieldname: "workstation", - options: "Workstation", - label: __("Workstation"), - get_query: () => { - const operation = dialog.get_value("operation"); - const filter_workstation = frm.doc.operations.filter(d => { - if (d.operation == operation) { - return d; - } - }); - - return { - filters: { - name: ["in", (filter_workstation || []).map(d => d.workstation)] - } - }; - }, - onchange: () => { - const operation = dialog.get_value("operation"); - const workstation = dialog.get_value("workstation"); - if (operation && workstation) { - const row = frm.doc.operations.filter(d => d.operation == operation && d.workstation == workstation)[0]; - qty = frm.doc.qty - row.completed_qty; - - if (qty > 0) { - dialog.set_value("qty", qty); - } - } - }, - reqd: true - }, { - fieldtype: "Float", - fieldname: "qty", - label: __("For Quantity"), - reqd: true - }]; - - const dialog = frappe.prompt(fields, function(data) { - if (data.qty > qty) { - frappe.throw(__("For Quantity must be less than quantity {0}", [qty])); + const dialog = frappe.prompt({fieldname: 'operations', fieldtype: 'Table', label: __('Operations'), + fields: [ + { + fieldtype:'Link', + fieldname:'operation', + label: __('Operation'), + read_only:1, + in_list_view:1 + }, + { + fieldtype:'Link', + fieldname:'workstation', + label: __('Workstation'), + read_only:1, + in_list_view:1 + }, + { + fieldtype:'Data', + fieldname:'name', + label: __('Operation Id') + }, + { + fieldtype:'Float', + fieldname:'pending_qty', + label: __('Pending Qty'), + }, + { + fieldtype:'Float', + fieldname:'qty', + label: __('Quantity to Manufacture'), + read_only:0, + in_list_view:1, + }, + ], + data: operations_data, + in_place_edit: true, + get_data: function() { + return operations_data; } - - if (data.qty <= 0) { - frappe.throw(__("For Quantity must be greater than zero")); - } - + }, function(data) { frappe.call({ method: "erpnext.manufacturing.doctype.work_order.work_order.make_job_card", args: { work_order: frm.doc.name, - operation: data.operation, - workstation: data.workstation, - qty: data.qty - }, - callback: function(r){ - if (r.message) { - var doc = frappe.model.sync(r.message)[0]; - frappe.set_route("Form", doc.doctype, doc.name); - } + operations: data.operations, } }); - }, __("For Job Card")); + }, __("Job Card"), __("Create")); + + var pending_qty = 0; + frm.doc.operations.forEach(data => { + if(data.completed_qty != frm.doc.qty) { + pending_qty = frm.doc.qty - flt(data.completed_qty); + + dialog.fields_dict.operations.df.data.push({ + 'name': data.name, + 'operation': data.operation, + 'workstation': data.workstation, + 'qty': pending_qty, + 'pending_qty': pending_qty, + }); + } + }); + dialog.fields_dict.operations.grid.refresh(); }, make_bom: function(frm) { @@ -277,7 +270,7 @@ frappe.ui.form.on("Work Order", { }); }, - show_progress: function(frm) { + show_progress_for_items: function(frm) { var bars = []; var message = ''; var added_min = false; @@ -311,6 +304,44 @@ frappe.ui.form.on("Work Order", { frm.dashboard.add_progress(__('Status'), bars, message); }, + show_progress_for_operations: function(frm) { + if (frm.doc.operations && frm.doc.operations.length) { + + let progress_class = { + "Work in Progress": "progress-bar-warning", + "Completed": "progress-bar-success" + }; + + let bars = []; + let message = ''; + let title = ''; + let status_wise_oprtation_data = {}; + let total_completed_qty = frm.doc.qty * frm.doc.operations.length; + + frm.doc.operations.forEach(d => { + if (!status_wise_oprtation_data[d.status]) { + status_wise_oprtation_data[d.status] = [d.completed_qty, d.operation]; + } else { + status_wise_oprtation_data[d.status][0] += d.completed_qty; + status_wise_oprtation_data[d.status][1] += ', ' + d.operation; + } + }); + + for (let key in status_wise_oprtation_data) { + title = __("{0} Operations: {1}", [key, status_wise_oprtation_data[key][1].bold()]); + bars.push({ + 'title': title, + 'width': status_wise_oprtation_data[key][0] / total_completed_qty * 100 + '%', + 'progress_class': progress_class[key] + }); + + message += title + '. '; + } + + frm.dashboard.add_progress(__('Status'), bars, message); + } + }, + production_item: function(frm) { if (frm.doc.production_item) { frappe.call({ @@ -395,6 +426,11 @@ frappe.ui.form.on("Work Order", { } }); } + }, + + additional_operating_cost: function(frm) { + erpnext.work_order.calculate_cost(frm.doc); + erpnext.work_order.calculate_total_cost(frm); } }); @@ -534,8 +570,7 @@ erpnext.work_order = { }, calculate_total_cost: function(frm) { - var variable_cost = frm.doc.actual_operating_cost ? - flt(frm.doc.actual_operating_cost) : flt(frm.doc.planned_operating_cost); + let variable_cost = flt(frm.doc.actual_operating_cost) || flt(frm.doc.planned_operating_cost); frm.set_value("total_operating_cost", (flt(frm.doc.additional_operating_cost) + variable_cost)); }, @@ -547,6 +582,7 @@ erpnext.work_order = { if (!r.exe) { frm.set_value("wip_warehouse", r.message.wip_warehouse); frm.set_value("fg_warehouse", r.message.fg_warehouse); + frm.set_value("scrap_warehouse", r.message.scrap_warehouse); } } }); @@ -577,6 +613,8 @@ erpnext.work_order = { description: __('Max: {0}', [max]), default: max }, data => { + max += (max * (frm.doc.__onload.overproduction_percentage || 0.0)) / 100; + if (data.qty > max) { frappe.msgprint(__('Quantity must not be more than {0}', [max])); reject(); diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json index 0d073a2312b..e6990fd985c 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.json +++ b/erpnext/manufacturing/doctype/work_order/work_order.json @@ -1,4 +1,5 @@ { + "actions": [], "allow_import": 1, "autoname": "naming_series:", "creation": "2013-01-10 16:34:16", @@ -28,9 +29,10 @@ "from_wip_warehouse", "update_consumed_material_cost_in_project", "warehouses", + "source_warehouse", "wip_warehouse", - "fg_warehouse", "column_break_12", + "fg_warehouse", "scrap_warehouse", "required_items_section", "required_items", @@ -225,12 +227,14 @@ "options": "fa fa-building" }, { + "description": "This is a location where operations are executed.", "fieldname": "wip_warehouse", "fieldtype": "Link", "label": "Work-in-Progress Warehouse", "options": "Warehouse" }, { + "description": "This is a location where final product stored.", "fieldname": "fg_warehouse", "fieldtype": "Link", "label": "Target Warehouse", @@ -241,6 +245,7 @@ "fieldtype": "Column Break" }, { + "description": "This is a location where scraped materials are stored.", "fieldname": "scrap_warehouse", "fieldtype": "Link", "label": "Scrap Warehouse", @@ -462,13 +467,21 @@ "fieldname": "update_consumed_material_cost_in_project", "fieldtype": "Check", "label": "Update Consumed Material Cost In Project" + }, + { + "description": "This is a location where raw materials are available.", + "fieldname": "source_warehouse", + "fieldtype": "Link", + "label": "Source Warehouse", + "options": "Warehouse" } ], "icon": "fa fa-cogs", "idx": 1, "image_field": "image", "is_submittable": 1, - "modified": "2019-08-28 12:29:35.315239", + "links": [], + "modified": "2020-01-31 12:46:23.636033", "modified_by": "Administrator", "module": "Manufacturing", "name": "Work Order", diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index ae4d9be2826..dd4a87266cb 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -6,14 +6,13 @@ import frappe import json import math from frappe import _ -from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate +from frappe.utils import flt, get_datetime, getdate, date_diff, cint, nowdate, get_link_to_form from frappe.model.document import Document from erpnext.manufacturing.doctype.bom.bom import validate_bom_no, get_bom_items_as_dict from dateutil.relativedelta import relativedelta from erpnext.stock.doctype.item.item import validate_end_of_life from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError from erpnext.projects.doctype.timesheet.timesheet import OverlapError -from erpnext.stock.doctype.stock_entry.stock_entry import get_additional_costs from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty from frappe.utils.csvutils import getlink @@ -22,6 +21,7 @@ from erpnext.utilities.transaction_base import validate_uom_is_integer from frappe.model.mapper import get_mapped_doc class OverProductionError(frappe.ValidationError): pass +class CapacityError(frappe.ValidationError): pass class StockOverProductionError(frappe.ValidationError): pass class OperationTooLongError(frappe.ValidationError): pass class ItemHasVariantError(frappe.ValidationError): pass @@ -37,7 +37,7 @@ class WorkOrder(Document): ms = frappe.get_doc("Manufacturing Settings") self.set_onload("material_consumption", ms.material_consumption) self.set_onload("backflush_raw_materials_based_on", ms.backflush_raw_materials_based_on) - + self.set_onload("overproduction_percentage", ms.overproduction_percentage_for_work_order) def validate(self): self.validate_production_item() @@ -216,14 +216,24 @@ class WorkOrder(Document): self.db_set(fieldname, qty) from erpnext.selling.doctype.sales_order.sales_order import update_produced_qty_in_so_item - update_produced_qty_in_so_item(self.sales_order_item) + + if self.sales_order and self.sales_order_item: + update_produced_qty_in_so_item(self.sales_order, self.sales_order_item) if self.production_plan: self.update_production_plan_status() def update_production_plan_status(self): production_plan = frappe.get_doc('Production Plan', self.production_plan) - production_plan.run_method("update_produced_qty", self.produced_qty, self.production_plan_item) + produced_qty = 0 + if self.production_plan_item: + total_qty = frappe.get_all("Work Order", fields = "sum(produced_qty) as produced_qty", + filters = {'docstatus': 1, 'production_plan': self.production_plan, + 'production_plan_item': self.production_plan_item}, as_list=1) + + produced_qty = total_qty[0][0] if total_qty else 0 + + production_plan.run_method("update_produced_qty", produced_qty, self.production_plan_item) def on_submit(self): if not self.wip_warehouse: @@ -250,12 +260,51 @@ class WorkOrder(Document): self.update_reserved_qty_for_production() def create_job_card(self): - for row in self.operations: + manufacturing_settings_doc = frappe.get_doc("Manufacturing Settings") + + enable_capacity_planning = not cint(manufacturing_settings_doc.disable_capacity_planning) + plan_days = cint(manufacturing_settings_doc.capacity_planning_for_days) or 30 + + for i, row in enumerate(self.operations): + self.set_operation_start_end_time(i, row) + if not row.workstation: frappe.throw(_("Row {0}: select the workstation against the operation {1}") .format(row.idx, row.operation)) - create_job_card(self, row, auto_create=True) + original_start_time = row.planned_start_time + job_card_doc = create_job_card(self, row, + enable_capacity_planning=enable_capacity_planning, auto_create=True) + + if enable_capacity_planning and job_card_doc: + row.planned_start_time = job_card_doc.time_logs[-1].from_time + row.planned_end_time = job_card_doc.time_logs[-1].to_time + + if date_diff(row.planned_start_time, original_start_time) > plan_days: + frappe.message_log.pop() + frappe.throw(_("Unable to find the time slot in the next {0} days for the operation {1}.") + .format(plan_days, row.operation), CapacityError) + + row.db_update() + + planned_end_date = self.operations and self.operations[-1].planned_end_time + if planned_end_date: + self.db_set("planned_end_date", planned_end_date) + + def set_operation_start_end_time(self, idx, row): + """Set start and end time for given operation. If first operation, set start as + `planned_start_date`, else add time diff to end time of earlier operation.""" + if idx==0: + # first operation at planned_start date + row.planned_start_time = self.planned_start_date + else: + row.planned_start_time = get_datetime(self.operations[idx-1].planned_end_time)\ + + get_mins_between_operations() + + row.planned_end_time = get_datetime(row.planned_start_time) + relativedelta(minutes = row.time_in_mins) + + if row.planned_start_time == row.planned_end_time: + frappe.throw(_("Capacity Planning Error, planned start time can not be same as end time")) def validate_cancel(self): if self.status == "Stopped": @@ -317,9 +366,8 @@ class WorkOrder(Document): """Fetch operations from BOM and set in 'Work Order'""" self.set('operations', []) - if not self.bom_no \ - or cint(frappe.db.get_single_value("Manufacturing Settings", "disable_capacity_planning")): - return + if not self.bom_no: + return if self.use_multi_level_bom: bom_list = frappe.get_doc("BOM", self.bom_no).traverse_tree() @@ -413,7 +461,7 @@ class WorkOrder(Document): def validate_operation_time(self): for d in self.operations: if not d.time_in_mins > 0: - frappe.throw(_("Operation Time must be greater than 0 for Operation {0}".format(d.operation))) + frappe.throw(_("Operation Time must be greater than 0 for Operation {0}").format(d.operation)) def update_required_items(self): ''' @@ -599,6 +647,23 @@ def get_item_details(item, project = None): return res +@frappe.whitelist() +def make_work_order(item, qty=0, project=None): + if not frappe.has_permission("Work Order", "write"): + frappe.throw(_("Not permitted"), frappe.PermissionError) + + item_details = get_item_details(item, project) + + wo_doc = frappe.new_doc("Work Order") + wo_doc.production_item = item + wo_doc.update(item_details) + + if flt(qty) > 0: + wo_doc.qty = flt(qty) + wo_doc.get_items_and_operations_from_bom() + + return wo_doc + @frappe.whitelist() def check_if_scrap_warehouse_mandatory(bom_no): res = {"set_scrap_wh_mandatory": False } @@ -619,8 +684,7 @@ def set_work_order_ops(name): @frappe.whitelist() def make_stock_entry(work_order_id, purpose, qty=None): work_order = frappe.get_doc("Work Order", work_order_id) - if not frappe.db.get_value("Warehouse", work_order.wip_warehouse, "is_group") \ - and not work_order.skip_transfer: + if not frappe.db.get_value("Warehouse", work_order.wip_warehouse, "is_group"): wip_warehouse = work_order.wip_warehouse else: wip_warehouse = None @@ -644,10 +708,6 @@ def make_stock_entry(work_order_id, purpose, qty=None): stock_entry.from_warehouse = wip_warehouse stock_entry.to_warehouse = work_order.fg_warehouse stock_entry.project = work_order.project - if purpose=="Manufacture": - additional_costs = get_additional_costs(work_order, fg_qty=stock_entry.fg_completed_qty, - company=work_order.company) - stock_entry.set("additional_costs", additional_costs) stock_entry.set_stock_entry_type() stock_entry.get_items() @@ -655,11 +715,13 @@ def make_stock_entry(work_order_id, purpose, qty=None): @frappe.whitelist() def get_default_warehouse(): - wip_warehouse = frappe.db.get_single_value("Manufacturing Settings", - "default_wip_warehouse") - fg_warehouse = frappe.db.get_single_value("Manufacturing Settings", - "default_fg_warehouse") - return {"wip_warehouse": wip_warehouse, "fg_warehouse": fg_warehouse} + doc = frappe.get_cached_doc("Manufacturing Settings") + + return { + "wip_warehouse": doc.default_wip_warehouse, + "fg_warehouse": doc.default_fg_warehouse, + "scrap_warehouse": doc.default_scrap_warehouse + } @frappe.whitelist() def stop_unstop(work_order, status): @@ -689,21 +751,41 @@ def query_sales_order(production_item): return out @frappe.whitelist() -def make_job_card(work_order, operation, workstation, qty=0): - work_order = frappe.get_doc('Work Order', work_order) - row = get_work_order_operation_data(work_order, operation, workstation) - if row: - return create_job_card(work_order, row, qty) +def make_job_card(work_order, operations): + if isinstance(operations, string_types): + operations = json.loads(operations) -def create_job_card(work_order, row, qty=0, auto_create=False): + work_order = frappe.get_doc('Work Order', work_order) + for row in operations: + validate_operation_data(row) + create_job_card(work_order, row, row.get("qty"), auto_create=True) + +def validate_operation_data(row): + if row.get("qty") <= 0: + frappe.throw(_("Quantity to Manufacture can not be zero for the operation {0}") + .format( + frappe.bold(row.get("operation")) + ) + ) + + if row.get("qty") > row.get("pending_qty"): + frappe.throw(_("For operation {0}: Quantity ({1}) can not be greter than pending quantity({2})") + .format( + frappe.bold(row.get("operation")), + frappe.bold(row.get("qty")), + frappe.bold(row.get("pending_qty")) + ) + ) + +def create_job_card(work_order, row, qty=0, enable_capacity_planning=False, auto_create=False): doc = frappe.new_doc("Job Card") doc.update({ 'work_order': work_order.name, - 'operation': row.operation, - 'workstation': row.workstation, + 'operation': row.get("operation"), + 'workstation': row.get("workstation"), 'posting_date': nowdate(), 'for_quantity': qty or work_order.get('qty', 0), - 'operation_id': row.name, + 'operation_id': row.get("name"), 'bom_no': work_order.bom_no, 'project': work_order.project, 'company': work_order.company, @@ -715,8 +797,11 @@ def create_job_card(work_order, row, qty=0, auto_create=False): if auto_create: doc.flags.ignore_mandatory = True + if enable_capacity_planning: + doc.schedule_time_logs(row) + doc.insert() - frappe.msgprint(_("Job card {0} created").format(doc.name)) + frappe.msgprint(_("Job card {0} created").format(get_link_to_form("Job Card", doc.name))) return doc @@ -766,4 +851,4 @@ def create_pick_list(source_name, target_doc=None, for_qty=None): doc.set_item_locations() - return doc \ No newline at end of file + return doc diff --git a/erpnext/manufacturing/doctype/work_order/work_order_calendar.js b/erpnext/manufacturing/doctype/work_order/work_order_calendar.js index c44b1e2700d..7ce05e9b881 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order_calendar.js +++ b/erpnext/manufacturing/doctype/work_order/work_order_calendar.js @@ -2,11 +2,13 @@ // For license information, please see license.txt frappe.views.calendar["Work Order"] = { + fields: ["planned_start_date", "planned_end_date", "status", "produced_qty", "qty", "name", "name"], field_map: { "start": "planned_start_date", "end": "planned_end_date", "id": "name", "title": "name", + "status": "status", "allDay": "allDay", "progress": function(data) { return flt(data.produced_qty) / data.qty * 100; diff --git a/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py b/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py index 0d3c30ea6eb..87c090f99c3 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py +++ b/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py @@ -6,7 +6,8 @@ def get_data(): 'fieldname': 'work_order', 'transactions': [ { - 'items': ['Pick List', 'Stock Entry', 'Job Card'] + 'label': _('Transactions'), + 'items': ['Stock Entry', 'Job Card', 'Pick List'] } ] } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json index 75d42cd061e..3f5e18e8130 100644 --- a/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json +++ b/erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2014-10-16 14:35:41.950175", "doctype": "DocType", "editable_grid": 1, @@ -68,6 +69,7 @@ "description": "Operation completed for how many finished goods?", "fieldname": "completed_qty", "fieldtype": "Float", + "in_list_view": 1, "label": "Completed Qty", "no_copy": 1, "read_only": 1 @@ -188,8 +190,9 @@ } ], "istable": 1, - "modified": "2019-07-16 23:01:07.720337", - "modified_by": "govindsmenokee@gmail.com", + "links": [], + "modified": "2019-12-03 19:24:29.594189", + "modified_by": "Administrator", "module": "Manufacturing", "name": "Work Order Operation", "owner": "Administrator", @@ -197,4 +200,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} +} \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/workstation/workstation.json b/erpnext/manufacturing/doctype/workstation/workstation.json index dca9891277f..d130391cece 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation.json +++ b/erpnext/manufacturing/doctype/workstation/workstation.json @@ -1,466 +1,159 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "field:workstation_name", - "beta": 0, - "creation": "2013-01-10 16:34:17", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Setup", - "editable_grid": 0, + "allow_import": 1, + "allow_rename": 1, + "autoname": "field:workstation_name", + "creation": "2013-01-10 16:34:17", + "doctype": "DocType", + "document_type": "Setup", + "field_order": [ + "workstation_name", + "production_capacity", + "column_break_3", + "over_heads", + "hour_rate_electricity", + "hour_rate_consumable", + "column_break_11", + "hour_rate_rent", + "hour_rate_labour", + "hour_rate", + "working_hours_section", + "holiday_list", + "working_hours", + "workstaion_description", + "description" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "description_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "workstation_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Workstation Name", + "oldfieldname": "workstation_name", + "oldfieldtype": "Data", + "reqd": 1, + "unique": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "workstation_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Workstation Name", - "length": 0, - "no_copy": 0, - "oldfieldname": "workstation_name", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "description", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Description", - "length": 0, - "no_copy": 0, - "oldfieldname": "description", - "oldfieldtype": "Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0, + "fieldname": "description", + "fieldtype": "Text", + "in_list_view": 1, + "label": "Description", + "oldfieldname": "description", + "oldfieldtype": "Text", "width": "300px" - }, + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "over_heads", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Operating Costs", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "over_heads", + "fieldtype": "Section Break", + "label": "Operating Costs", + "oldfieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "per hour", - "fieldname": "hour_rate_electricity", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Electricity Cost", - "length": 0, - "no_copy": 0, - "oldfieldname": "hour_rate_electricity", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "bold": 1, + "description": "per hour", + "fieldname": "hour_rate_electricity", + "fieldtype": "Currency", + "label": "Electricity Cost", + "oldfieldname": "hour_rate_electricity", + "oldfieldtype": "Currency" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "per hour", - "fieldname": "hour_rate_consumable", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Consumable Cost", - "length": 0, - "no_copy": 0, - "oldfieldname": "hour_rate_consumable", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "bold": 1, + "description": "per hour", + "fieldname": "hour_rate_consumable", + "fieldtype": "Currency", + "label": "Consumable Cost", + "oldfieldname": "hour_rate_consumable", + "oldfieldtype": "Currency" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_11", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "per hour", - "fieldname": "hour_rate_rent", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rent Cost", - "length": 0, - "no_copy": 0, - "oldfieldname": "hour_rate_rent", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "bold": 1, + "description": "per hour", + "fieldname": "hour_rate_rent", + "fieldtype": "Currency", + "label": "Rent Cost", + "oldfieldname": "hour_rate_rent", + "oldfieldtype": "Currency" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Wages per hour", - "fieldname": "hour_rate_labour", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Wages", - "length": 0, - "no_copy": 0, - "oldfieldname": "hour_rate_labour", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "bold": 1, + "description": "Wages per hour", + "fieldname": "hour_rate_labour", + "fieldtype": "Currency", + "label": "Wages", + "oldfieldname": "hour_rate_labour", + "oldfieldtype": "Currency" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "per hour", - "fieldname": "hour_rate", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Net Hour Rate", - "length": 0, - "no_copy": 0, - "oldfieldname": "hour_rate", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "description": "per hour", + "fieldname": "hour_rate", + "fieldtype": "Currency", + "label": "Net Hour Rate", + "oldfieldname": "hour_rate", + "oldfieldtype": "Currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "working_hours_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Working Hours", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "working_hours_section", + "fieldtype": "Section Break", + "label": "Working Hours" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "working_hours", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Working Hours", - "length": 0, - "no_copy": 0, - "options": "Workstation Working Hour", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "working_hours", + "fieldtype": "Table", + "label": "Working Hours", + "options": "Workstation Working Hour" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "holiday_list", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Holiday List", - "length": 0, - "no_copy": 0, - "options": "Holiday List", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "holiday_list", + "fieldtype": "Link", + "label": "Holiday List", + "options": "Holiday List" + }, + { + "default": "1", + "fieldname": "production_capacity", + "fieldtype": "Int", + "label": "Production Capacity", + "reqd": 1 + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "fieldname": "workstaion_description", + "fieldtype": "Section Break", + "label": "Description" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "icon-wrench", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2017-07-18 22:28:50.163219", - "modified_by": "Administrator", - "module": "Manufacturing", - "name": "Workstation", - "owner": "Administrator", + ], + "icon": "icon-wrench", + "idx": 1, + "modified": "2019-11-26 12:39:19.742052", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Workstation", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "apply_user_permissions": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Manufacturing User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "share": 1, "write": 1 } - ], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 1, - "sort_order": "ASC", - "track_changes": 1, - "track_seen": 0 + ], + "quick_entry": 1, + "show_name_in_global_search": 1, + "sort_order": "ASC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/workstation/workstation.py b/erpnext/manufacturing/doctype/workstation/workstation.py index f6ab72ade49..3512e590458 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation.py +++ b/erpnext/manufacturing/doctype/workstation/workstation.py @@ -4,7 +4,9 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import flt, cint, getdate, formatdate, comma_and, time_diff_in_seconds, to_timedelta +from erpnext.support.doctype.issue.issue import get_holidays +from frappe.utils import (flt, cint, getdate, formatdate, + comma_and, time_diff_in_seconds, to_timedelta, add_days) from frappe.model.document import Document from dateutil.parser import parse @@ -43,6 +45,17 @@ class Workstation(Document): where parent = %s and workstation = %s""", (self.hour_rate, bom_no[0], self.name)) + def validate_workstation_holiday(self, schedule_date, skip_holiday_list_check=False): + if not skip_holiday_list_check and (not self.holiday_list or + cint(frappe.db.get_single_value("Manufacturing Settings", "allow_production_on_holidays"))): + return schedule_date + + if schedule_date in tuple(get_holidays(self.holiday_list)): + schedule_date = add_days(schedule_date, 1) + self.validate_workstation_holiday(schedule_date, skip_holiday_list_check=True) + + return schedule_date + @frappe.whitelist() def get_default_holiday_list(): return frappe.get_cached_value('Company', frappe.defaults.get_user_default("Company"), "default_holiday_list") diff --git a/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py b/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py index 9e0d1d17394..3ddbe731700 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py +++ b/erpnext/manufacturing/doctype/workstation/workstation_dashboard.py @@ -6,8 +6,14 @@ def get_data(): 'fieldname': 'workstation', 'transactions': [ { - 'label': _('Manufacture'), - 'items': ['BOM', 'Routing', 'Work Order', 'Job Card', 'Operation', 'Timesheet'] + 'label': _('Master'), + 'items': ['BOM', 'Routing', 'Operation'] + }, + { + 'label': _('Transaction'), + 'items': ['Work Order', 'Job Card', 'Timesheet'] } - ] + ], + 'disable_create_buttons': ['BOM', 'Routing', 'Operation', + 'Work Order', 'Job Card', 'Timesheet'] } diff --git a/erpnext/manufacturing/report/bom_explorer/bom_explorer.py b/erpnext/manufacturing/report/bom_explorer/bom_explorer.py index 875d1152dea..48907adc5f3 100644 --- a/erpnext/manufacturing/report/bom_explorer/bom_explorer.py +++ b/erpnext/manufacturing/report/bom_explorer/bom_explorer.py @@ -14,7 +14,7 @@ def execute(filters=None): def get_data(filters, data): get_exploded_items(filters.bom, data) -def get_exploded_items(bom, data, indent=0): +def get_exploded_items(bom, data, indent=0, qty=1): exploded_items = frappe.get_all("BOM Item", filters={"parent": bom}, fields= ['qty','bom_no','qty','scrap','item_code','item_name','description','uom']) @@ -26,13 +26,13 @@ def get_exploded_items(bom, data, indent=0): 'item_name': item.item_name, 'indent': indent, 'bom': item.bom_no, - 'qty': item.qty, + 'qty': item.qty * qty, 'uom': item.uom, 'description': item.description, 'scrap': item.scrap }) if item.bom_no: - get_exploded_items(item.bom_no, data, indent=indent+1) + get_exploded_items(item.bom_no, data, indent=indent+1, qty=item.qty) def get_columns(): return [ diff --git a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py index be016ad9a47..f7b407b7922 100644 --- a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py +++ b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ +from frappe.utils.data import comma_and def execute(filters=None): # if not filters: filters = {} @@ -13,35 +14,36 @@ def execute(filters=None): data = get_bom_stock(filters) qty_to_make = filters.get("qty_to_make") + manufacture_details = get_manufacturer_records() for row in data: - item_map = get_item_details(row.item_code) reqd_qty = qty_to_make * row.actual_qty last_pur_price = frappe.db.get_value("Item", row.item_code, "last_purchase_rate") - if row.to_build > 0: - diff_qty = row.to_build - reqd_qty - summ_data.append([row.item_code, row.description, item_map[row.item_code]["manufacturer"], item_map[row.item_code]["manufacturer_part_no"], row.actual_qty, row.to_build, reqd_qty, diff_qty, last_pur_price]) - else: - diff_qty = 0 - reqd_qty - summ_data.append([row.item_code, row.description, item_map[row.item_code]["manufacturer"], item_map[row.item_code]["manufacturer_part_no"], row.actual_qty, "0.000", reqd_qty, diff_qty, last_pur_price]) + summ_data.append(get_report_data(last_pur_price, reqd_qty, row, manufacture_details)) return columns, summ_data +def get_report_data(last_pur_price, reqd_qty, row, manufacture_details): + to_build = row.to_build if row.to_build > 0 else 0 + diff_qty = to_build - reqd_qty + return [row.item_code, row.description, + comma_and(manufacture_details.get(row.item_code, {}).get('manufacturer', []), add_quotes=False), + comma_and(manufacture_details.get(row.item_code, {}).get('manufacturer_part', []), add_quotes=False), + row.actual_qty, str(to_build), + reqd_qty, diff_qty, last_pur_price] + def get_columns(): """return columns""" columns = [ _("Item") + ":Link/Item:100", _("Description") + "::150", - _("Manufacturer") + "::100", - _("Manufacturer Part Number") + "::100", + _("Manufacturer") + "::250", + _("Manufacturer Part Number") + "::250", _("Qty") + ":Float:50", _("Stock Qty") + ":Float:100", _("Reqd Qty")+ ":Float:100", _("Diff Qty")+ ":Float:100", _("Last Purchase Price")+ ":Float:100", - - ] - return columns def get_bom_stock(filters): @@ -85,7 +87,12 @@ def get_bom_stock(filters): GROUP BY bom_item.item_code""".format(qty_field=qty_field, table=table, conditions=conditions, bom=bom), as_dict=1) -def get_item_details(item_code): - items = frappe.db.sql("""select it.item_group, it.item_name, it.stock_uom, it.name, it.brand, it.description, it.manufacturer_part_no, it.manufacturer from tabItem it where it.item_code = %s""", item_code, as_dict=1) +def get_manufacturer_records(): + details = frappe.get_list('Item Manufacturer', fields = ["manufacturer", "manufacturer_part_no, parent"]) + manufacture_details = frappe._dict() + for detail in details: + dic = manufacture_details.setdefault(detail.get('parent'), {}) + dic.setdefault('manufacturer', []).append(detail.get('manufacturer')) + dic.setdefault('manufacturer_part', []).append(detail.get('manufacturer_part_no')) - return dict((d.name, d) for d in items) + return manufacture_details \ No newline at end of file diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 428bb7f4eef..a8938406f29 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -13,6 +13,7 @@ erpnext.patches.v4_0.apply_user_permissions erpnext.patches.v4_0.move_warehouse_user_to_restrictions erpnext.patches.v4_0.global_defaults_to_system_settings erpnext.patches.v4_0.update_incharge_name_to_sales_person_in_maintenance_schedule +execute:frappe.reload_doc("HR", "doctype", "HR Settings") #2020-01-16 execute:frappe.reload_doc('stock', 'doctype', 'warehouse') # 2017-04-24 execute:frappe.reload_doc('accounts', 'doctype', 'sales_invoice') # 2016-08-31 execute:frappe.reload_doc('selling', 'doctype', 'sales_order') # 2014-01-29 @@ -513,7 +514,6 @@ erpnext.patches.v11_0.rename_employee_loan_to_loan erpnext.patches.v11_0.move_leave_approvers_from_employee #13-06-2018 erpnext.patches.v11_0.update_department_lft_rgt erpnext.patches.v11_0.add_default_email_template_for_leave -execute:frappe.reload_doc("HR", "doctype", "HR Settings") erpnext.patches.v11_0.set_default_email_template_in_hr #08-06-2018 erpnext.patches.v11_0.uom_conversion_data #30-06-2018 erpnext.patches.v10_0.taxes_issue_with_pos @@ -586,6 +586,7 @@ erpnext.patches.v11_0.add_permissions_in_gst_settings erpnext.patches.v11_1.setup_guardian_role execute:frappe.delete_doc('DocType', 'Notification Control') erpnext.patches.v12_0.set_gst_category +erpnext.patches.v12_0.update_gst_category erpnext.patches.v11_0.remove_barcodes_field_from_copy_fields_to_variants erpnext.patches.v12_0.set_task_status erpnext.patches.v11_0.make_italian_localization_fields # 26-03-2019 @@ -638,10 +639,21 @@ erpnext.patches.v12_0.add_variant_of_in_item_attribute_table erpnext.patches.v12_0.rename_bank_account_field_in_journal_entry_account erpnext.patches.v12_0.create_default_energy_point_rules erpnext.patches.v12_0.set_produced_qty_field_in_sales_order_for_work_order -erpnext.patches.v12_0.generate_leave_ledger_entries erpnext.patches.v12_0.set_default_shopify_app_type +erpnext.patches.v12_0.set_cwip_and_delete_asset_settings erpnext.patches.v12_0.set_expense_account_in_landed_cost_voucher_taxes erpnext.patches.v12_0.replace_accounting_with_accounts_in_home_settings erpnext.patches.v12_0.set_payment_entry_status erpnext.patches.v12_0.update_owner_fields_in_acc_dimension_custom_fields -erpnext.patches.v12_0.create_irs_1099_field_united_states +erpnext.patches.v12_0.add_export_type_field_in_party_master +erpnext.patches.v12_0.remove_denied_leaves_from_leave_ledger +erpnext.patches.v12_0.update_price_or_product_discount +erpnext.patches.v12_0.set_production_capacity_in_workstation +erpnext.patches.v12_0.set_employee_preferred_emails +erpnext.patches.v12_0.set_against_blanket_order_in_sales_and_purchase_order +erpnext.patches.v12_0.set_cost_center_in_child_table_of_expense_claim +erpnext.patches.v12_0.set_lead_title_field +erpnext.patches.v12_0.set_permission_einvoicing +erpnext.patches.v12_0.set_published_in_hub_tracked_item +erpnext.patches.v12_0.set_job_offer_applicant_email +erpnext.patches.v12_0.create_irs_1099_field_united_states \ No newline at end of file diff --git a/erpnext/patches/v11_0/make_asset_finance_book_against_old_entries.py b/erpnext/patches/v11_0/make_asset_finance_book_against_old_entries.py index 1c8bd689329..ee709ac2d49 100644 --- a/erpnext/patches/v11_0/make_asset_finance_book_against_old_entries.py +++ b/erpnext/patches/v11_0/make_asset_finance_book_against_old_entries.py @@ -17,10 +17,6 @@ def execute(): frappe.db.sql(""" update `tabAsset` ast, `tabWarehouse` wh set ast.location = wh.warehouse_name where ast.warehouse = wh.name""") - frappe.db.sql(""" update `tabAsset Movement` ast_mv - set ast_mv.source_location = (select warehouse_name from `tabWarehouse` where name = ast_mv.source_warehouse), - ast_mv.target_location = (select warehouse_name from `tabWarehouse` where name = ast_mv.target_warehouse)""") - for d in frappe.get_all('Asset'): doc = frappe.get_doc('Asset', d.name) if doc.calculate_depreciation: diff --git a/erpnext/patches/v11_0/merge_land_unit_with_location.py b/erpnext/patches/v11_0/merge_land_unit_with_location.py index 1ea486dfd5d..7845da255a8 100644 --- a/erpnext/patches/v11_0/merge_land_unit_with_location.py +++ b/erpnext/patches/v11_0/merge_land_unit_with_location.py @@ -4,19 +4,18 @@ from __future__ import unicode_literals import frappe -from frappe.model.rename_doc import rename_doc from frappe.model.utils.rename_field import rename_field def execute(): # Rename and reload the Land Unit and Linked Land Unit doctypes if frappe.db.table_exists('Land Unit') and not frappe.db.table_exists('Location'): - rename_doc('DocType', 'Land Unit', 'Location', force=True) + frappe.rename_doc('DocType', 'Land Unit', 'Location', force=True) frappe.reload_doc('assets', 'doctype', 'location') if frappe.db.table_exists('Linked Land Unit') and not frappe.db.table_exists('Linked Location'): - rename_doc('DocType', 'Linked Land Unit', 'Linked Location', force=True) + frappe.rename_doc('DocType', 'Linked Land Unit', 'Linked Location', force=True) frappe.reload_doc('assets', 'doctype', 'linked_location') diff --git a/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py b/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py index c03ab0b7111..fad0cf7a45e 100644 --- a/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py +++ b/erpnext/patches/v11_0/rename_asset_adjustment_doctype.py @@ -3,9 +3,9 @@ from __future__ import unicode_literals import frappe -from frappe.model.rename_doc import rename_doc + def execute(): if frappe.db.table_exists("Asset Adjustment") and not frappe.db.table_exists("Asset Value Adjustment"): - rename_doc('DocType', 'Asset Adjustment', 'Asset Value Adjustment', force=True) + frappe.rename_doc('DocType', 'Asset Adjustment', 'Asset Value Adjustment', force=True) frappe.reload_doc('assets', 'doctype', 'asset_value_adjustment') \ No newline at end of file diff --git a/erpnext/patches/v11_0/rename_bom_wo_fields.py b/erpnext/patches/v11_0/rename_bom_wo_fields.py index c8106a6bd59..b4a740fabbf 100644 --- a/erpnext/patches/v11_0/rename_bom_wo_fields.py +++ b/erpnext/patches/v11_0/rename_bom_wo_fields.py @@ -15,13 +15,6 @@ def execute(): rename_field(doctype, "allow_transfer_for_manufacture", "include_item_in_manufacturing") - if frappe.db.has_column('BOM', 'allow_same_item_multiple_times'): - frappe.db.sql(""" UPDATE tabBOM - SET - allow_same_item_multiple_times = 0 - WHERE - trim(coalesce(allow_same_item_multiple_times, '')) = '' """) - for doctype in ['BOM', 'Work Order']: frappe.reload_doc('manufacturing', 'doctype', frappe.scrub(doctype)) diff --git a/erpnext/patches/v11_0/rename_health_insurance.py b/erpnext/patches/v11_0/rename_health_insurance.py index 24d1ddf0314..e605071a297 100644 --- a/erpnext/patches/v11_0/rename_health_insurance.py +++ b/erpnext/patches/v11_0/rename_health_insurance.py @@ -2,9 +2,8 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -from frappe.model.rename_doc import rename_doc import frappe def execute(): - rename_doc('DocType', 'Health Insurance', 'Employee Health Insurance', force=True) + frappe.rename_doc('DocType', 'Health Insurance', 'Employee Health Insurance', force=True) frappe.reload_doc('hr', 'doctype', 'employee_health_insurance') \ No newline at end of file diff --git a/erpnext/patches/v11_0/rename_healthcare_doctype_and_fields.py b/erpnext/patches/v11_0/rename_healthcare_doctype_and_fields.py index 8fdac07658f..9705681b33f 100644 --- a/erpnext/patches/v11_0/rename_healthcare_doctype_and_fields.py +++ b/erpnext/patches/v11_0/rename_healthcare_doctype_and_fields.py @@ -1,6 +1,5 @@ from __future__ import unicode_literals import frappe -from frappe.model.rename_doc import rename_doc from frappe.model.utils.rename_field import rename_field from frappe.modules import scrub, get_doctype_module @@ -37,7 +36,7 @@ doc_rename_map = { def execute(): for dt in doc_rename_map: if frappe.db.exists('DocType', dt): - rename_doc('DocType', dt, doc_rename_map[dt], force=True) + frappe.rename_doc('DocType', dt, doc_rename_map[dt], force=True) for dn in field_rename_map: if frappe.db.exists('DocType', dn): diff --git a/erpnext/patches/v11_0/rename_production_order_to_work_order.py b/erpnext/patches/v11_0/rename_production_order_to_work_order.py index 2c27fbbc9df..2f620f413ba 100644 --- a/erpnext/patches/v11_0/rename_production_order_to_work_order.py +++ b/erpnext/patches/v11_0/rename_production_order_to_work_order.py @@ -2,18 +2,17 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -from frappe.model.rename_doc import rename_doc -from frappe.model.utils.rename_field import rename_field import frappe +from frappe.model.utils.rename_field import rename_field def execute(): - rename_doc('DocType', 'Production Order', 'Work Order', force=True) + frappe.rename_doc('DocType', 'Production Order', 'Work Order', force=True) frappe.reload_doc('manufacturing', 'doctype', 'work_order') - rename_doc('DocType', 'Production Order Item', 'Work Order Item', force=True) + frappe.rename_doc('DocType', 'Production Order Item', 'Work Order Item', force=True) frappe.reload_doc('manufacturing', 'doctype', 'work_order_item') - rename_doc('DocType', 'Production Order Operation', 'Work Order Operation', force=True) + frappe.rename_doc('DocType', 'Production Order Operation', 'Work Order Operation', force=True) frappe.reload_doc('manufacturing', 'doctype', 'work_order_operation') frappe.reload_doc('projects', 'doctype', 'timesheet') diff --git a/erpnext/patches/v11_0/rename_supplier_type_to_supplier_group.py b/erpnext/patches/v11_0/rename_supplier_type_to_supplier_group.py index 52d4621c7b7..c4b3838c71d 100644 --- a/erpnext/patches/v11_0/rename_supplier_type_to_supplier_group.py +++ b/erpnext/patches/v11_0/rename_supplier_type_to_supplier_group.py @@ -1,6 +1,5 @@ from __future__ import unicode_literals import frappe -from frappe.model.rename_doc import rename_doc from frappe.model.utils.rename_field import rename_field from frappe import _ from frappe.utils.nestedset import rebuild_tree @@ -9,7 +8,7 @@ def execute(): if frappe.db.table_exists("Supplier Group"): frappe.reload_doc('setup', 'doctype', 'supplier_group') elif frappe.db.table_exists("Supplier Type"): - rename_doc("DocType", "Supplier Type", "Supplier Group", force=True) + frappe.rename_doc("DocType", "Supplier Type", "Supplier Group", force=True) frappe.reload_doc('setup', 'doctype', 'supplier_group') frappe.reload_doc("accounts", "doctype", "pricing_rule") frappe.reload_doc("accounts", "doctype", "tax_rule") diff --git a/erpnext/patches/v12_0/add_export_type_field_in_party_master.py b/erpnext/patches/v12_0/add_export_type_field_in_party_master.py new file mode 100644 index 00000000000..5bb6e3fb339 --- /dev/null +++ b/erpnext/patches/v12_0/add_export_type_field_in_party_master.py @@ -0,0 +1,42 @@ +from __future__ import unicode_literals +import frappe +from erpnext.regional.india.setup import make_custom_fields + +def execute(): + + company = frappe.get_all('Company', filters = {'country': 'India'}) + if not company: + return + + make_custom_fields() + + frappe.reload_doctype('Tax Category') + frappe.reload_doctype('Sales Taxes and Charges Template') + frappe.reload_doctype('Purchase Taxes and Charges Template') + + # Create tax category with inter state field checked + tax_category = frappe.db.get_value('Tax Category', {'name': 'OUT OF STATE'}, 'name') + + if not tax_category: + inter_state_category = frappe.get_doc({ + 'doctype': 'Tax Category', + 'title': 'OUT OF STATE', + 'is_inter_state': 1 + }).insert() + + tax_category = inter_state_category.name + + for doctype in ('Sales Taxes and Charges Template', 'Purchase Taxes and Charges Template'): + if not frappe.get_meta(doctype).has_field('is_inter_state'): continue + + template = frappe.db.get_value(doctype, {'is_inter_state': 1, 'disabled': 0}, ['name']) + if template: + frappe.db.set_value(doctype, template, 'tax_category', tax_category) + + frappe.db.sql(""" + DELETE FROM `tabCustom Field` + WHERE fieldname = 'is_inter_state' + AND dt IN ('Sales Taxes and Charges Template', 'Purchase Taxes and Charges Template') + """) + + diff --git a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py index 412f32030ae..31d500ea2f7 100644 --- a/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py +++ b/erpnext/patches/v12_0/move_item_tax_to_item_tax_template.py @@ -1,20 +1,30 @@ import frappe import json from six import iteritems +from frappe.model.naming import make_autoname def execute(): if "tax_type" not in frappe.db.get_table_columns("Item Tax"): return old_item_taxes = {} item_tax_templates = {} - rename_template_to_untitled = [] + + frappe.reload_doc("accounts", "doctype", "item_tax_template_detail", force=1) + frappe.reload_doc("accounts", "doctype", "item_tax_template", force=1) + existing_templates = frappe.db.sql("""select template.name, details.tax_type, details.tax_rate + from `tabItem Tax Template` template, `tabItem Tax Template Detail` details + where details.parent=template.name + """, as_dict=1) + + if len(existing_templates): + for d in existing_templates: + item_tax_templates.setdefault(d.name, {}) + item_tax_templates[d.name][d.tax_type] = d.tax_rate for d in frappe.db.sql("""select parent as item_code, tax_type, tax_rate from `tabItem Tax`""", as_dict=1): old_item_taxes.setdefault(d.item_code, []) old_item_taxes[d.item_code].append(d) - frappe.reload_doc("accounts", "doctype", "item_tax_template_detail", force=1) - frappe.reload_doc("accounts", "doctype", "item_tax_template", force=1) frappe.reload_doc("stock", "doctype", "item", force=1) frappe.reload_doc("stock", "doctype", "item_tax", force=1) frappe.reload_doc("selling", "doctype", "quotation_item", force=1) @@ -27,6 +37,8 @@ def execute(): frappe.reload_doc("accounts", "doctype", "purchase_invoice_item", force=1) frappe.reload_doc("accounts", "doctype", "accounts_settings", force=1) + frappe.db.auto_commit_on_many_writes = True + # for each item that have item tax rates for item_code in old_item_taxes.keys(): # make current item's tax map @@ -34,68 +46,95 @@ def execute(): for d in old_item_taxes[item_code]: item_tax_map[d.tax_type] = d.tax_rate - item_tax_template_name = get_item_tax_template(item_tax_templates, rename_template_to_untitled, - item_tax_map, item_code) + item_tax_template_name = get_item_tax_template(item_tax_templates, item_tax_map, item_code) # update the item tax table - item = frappe.get_doc("Item", item_code) - item.set("taxes", []) - item.append("taxes", {"item_tax_template": item_tax_template_name, "tax_category": ""}) frappe.db.sql("delete from `tabItem Tax` where parent=%s and parenttype='Item'", item_code) - for d in item.taxes: - d.db_insert() + if item_tax_template_name: + item = frappe.get_doc("Item", item_code) + item.set("taxes", []) + item.append("taxes", {"item_tax_template": item_tax_template_name, "tax_category": ""}) + for d in item.taxes: + d.db_insert() doctypes = [ 'Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice', 'Supplier Quotation', 'Purchase Order', 'Purchase Receipt', 'Purchase Invoice' ] - for dt in doctypes: - for d in frappe.db.sql("""select name, parent, item_code, item_tax_rate from `tab{0} Item` - where ifnull(item_tax_rate, '') not in ('', '{{}}')""".format(dt), as_dict=1): - item_tax_map = json.loads(d.item_tax_rate) - item_tax_template = get_item_tax_template(item_tax_templates, rename_template_to_untitled, - item_tax_map, d.item_code, d.parent) - frappe.db.set_value(dt + " Item", d.name, "item_tax_template", item_tax_template) - idx = 1 - for oldname in rename_template_to_untitled: - frappe.rename_doc("Item Tax Template", oldname, "Untitled {}".format(idx)) - idx += 1 + for dt in doctypes: + for d in frappe.db.sql("""select name, parenttype, parent, item_code, item_tax_rate from `tab{0} Item` + where ifnull(item_tax_rate, '') not in ('', '{{}}') + and item_tax_template is NULL""".format(dt), as_dict=1): + item_tax_map = json.loads(d.item_tax_rate) + item_tax_template_name = get_item_tax_template(item_tax_templates, + item_tax_map, d.item_code, d.parenttype, d.parent) + frappe.db.set_value(dt + " Item", d.name, "item_tax_template", item_tax_template_name) + + frappe.db.auto_commit_on_many_writes = False settings = frappe.get_single("Accounts Settings") settings.add_taxes_from_item_tax_template = 0 settings.determine_address_tax_category_from = "Billing Address" settings.save() -def get_item_tax_template(item_tax_templates, rename_template_to_untitled, item_tax_map, item_code, parent=None): +def get_item_tax_template(item_tax_templates, item_tax_map, item_code, parenttype=None, parent=None): # search for previously created item tax template by comparing tax maps for template, item_tax_template_map in iteritems(item_tax_templates): if item_tax_map == item_tax_template_map: - if not parent: - rename_template_to_untitled.append(template) return template # if no item tax template found, create one item_tax_template = frappe.new_doc("Item Tax Template") - item_tax_template.title = "{}--{}".format(parent, item_code) if parent else "Item-{}".format(item_code) + item_tax_template.title = make_autoname("Item Tax Template-.####") + for tax_type, tax_rate in iteritems(item_tax_map): - if not frappe.db.exists("Account", tax_type): + account_details = frappe.db.get_value("Account", tax_type, ['name', 'account_type'], as_dict=1) + if account_details: + if account_details.account_type not in ('Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation'): + frappe.db.set_value('Account', account_details.name, 'account_type', 'Chargeable') + else: parts = tax_type.strip().split(" - ") account_name = " - ".join(parts[:-1]) - company = frappe.db.get_value("Company", filters={"abbr": parts[-1]}) - parent_account = frappe.db.get_value("Account", - filters={"account_type": "Tax", "root_type": "Liability", "is_group": 0, "company": company}, fieldname="parent_account") + if not account_name: + tax_type = None + else: + company = get_company(parts[-1], parenttype, parent) + parent_account = frappe.db.get_value("Account", + filters={"account_type": "Tax", "root_type": "Liability", "is_group": 0, "company": company}, fieldname="parent_account") + if not parent_account: + parent_account = frappe.db.get_value("Account", + filters={"account_type": "Tax", "root_type": "Liability", "is_group": 1, "company": company}) + filters = { + "account_name": account_name, + "company": company, + "account_type": "Tax", + "parent_account": parent_account + } + tax_type = frappe.db.get_value("Account", filters) + if not tax_type: + account = frappe.new_doc("Account") + account.update(filters) + account.insert() + tax_type = account.name - frappe.get_doc({ - "doctype": "Account", - "account_name": account_name, - "company": company, - "account_type": "Tax", - "parent_account": parent_account - }).insert() + if tax_type: + item_tax_template.append("taxes", {"tax_type": tax_type, "tax_rate": tax_rate}) + item_tax_templates.setdefault(item_tax_template.title, {}) + item_tax_templates[item_tax_template.title][tax_type] = tax_rate + if item_tax_template.get("taxes"): + item_tax_template.save() + return item_tax_template.name - item_tax_template.append("taxes", {"tax_type": tax_type, "tax_rate": tax_rate}) - item_tax_templates.setdefault(item_tax_template.title, {}) - item_tax_templates[item_tax_template.title][tax_type] = tax_rate - item_tax_template.save() - return item_tax_template.name +def get_company(company_abbr, parenttype=None, parent=None): + if parenttype and parent: + company = frappe.get_cached_value(parenttype, parent, 'company') + else: + company = frappe.db.get_value("Company", filters={"abbr": company_abbr}) + + if not company: + companies = frappe.get_all('Company') + if len(companies) == 1: + company = companies[0].name + + return company diff --git a/erpnext/patches/v12_0/remove_denied_leaves_from_leave_ledger.py b/erpnext/patches/v12_0/remove_denied_leaves_from_leave_ledger.py new file mode 100644 index 00000000000..7859606e5cb --- /dev/null +++ b/erpnext/patches/v12_0/remove_denied_leaves_from_leave_ledger.py @@ -0,0 +1,28 @@ +# Copyright (c) 2018, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe +from frappe.utils import getdate, today + +def execute(): + ''' Delete leave ledger entry created + via leave applications with status != Approved ''' + if not frappe.db.a_row_exists("Leave Ledger Entry"): + return + + leave_application_list = get_denied_leave_application_list() + if leave_application_list: + delete_denied_leaves_from_leave_ledger_entry(leave_application_list) + +def get_denied_leave_application_list(): + return frappe.db.sql_list(''' Select name from `tabLeave Application` where status <> 'Approved' ''') + +def delete_denied_leaves_from_leave_ledger_entry(leave_application_list): + if leave_application_list: + frappe.db.sql(''' Delete + FROM `tabLeave Ledger Entry` + WHERE + transaction_type = 'Leave Application' + AND transaction_name in (%s) ''' % (', '.join(['%s'] * len(leave_application_list))), #nosec + tuple(leave_application_list)) \ No newline at end of file diff --git a/erpnext/patches/v12_0/rename_pricing_rule_child_doctypes.py b/erpnext/patches/v12_0/rename_pricing_rule_child_doctypes.py index 41ac8cff2b4..b9ad622b0ea 100644 --- a/erpnext/patches/v12_0/rename_pricing_rule_child_doctypes.py +++ b/erpnext/patches/v12_0/rename_pricing_rule_child_doctypes.py @@ -3,7 +3,6 @@ from __future__ import unicode_literals import frappe -from frappe.model.rename_doc import rename_doc doctypes = { 'Price Discount Slab': 'Promotional Scheme Price Discount', @@ -16,6 +15,6 @@ doctypes = { def execute(): for old_doc, new_doc in doctypes.items(): if not frappe.db.table_exists(new_doc) and frappe.db.table_exists(old_doc): - rename_doc('DocType', old_doc, new_doc) + frappe.rename_doc('DocType', old_doc, new_doc) frappe.reload_doc("accounts", "doctype", frappe.scrub(new_doc)) frappe.delete_doc("DocType", old_doc) diff --git a/erpnext/patches/v12_0/set_against_blanket_order_in_sales_and_purchase_order.py b/erpnext/patches/v12_0/set_against_blanket_order_in_sales_and_purchase_order.py new file mode 100644 index 00000000000..3fccbfa7003 --- /dev/null +++ b/erpnext/patches/v12_0/set_against_blanket_order_in_sales_and_purchase_order.py @@ -0,0 +1,12 @@ +import frappe +def execute(): + + frappe.reload_doc('selling', 'doctype', frappe.scrub('Sales Order Item')) + frappe.reload_doc('buying', 'doctype', frappe.scrub('Purchase Order Item')) + + for doctype in ['Sales Order Item', 'Purchase Order Item']: + frappe.db.sql(""" + UPDATE `tab{0}` + SET against_blanket_order = 1 + WHERE ifnull(blanket_order, '') != '' + """.format(doctype)) diff --git a/erpnext/patches/v12_0/set_cost_center_in_child_table_of_expense_claim.py b/erpnext/patches/v12_0/set_cost_center_in_child_table_of_expense_claim.py new file mode 100644 index 00000000000..8ba0d79a831 --- /dev/null +++ b/erpnext/patches/v12_0/set_cost_center_in_child_table_of_expense_claim.py @@ -0,0 +1,8 @@ +import frappe +def execute(): + frappe.reload_doc('hr', 'doctype', 'expense_claim_detail') + frappe.db.sql(""" + UPDATE `tabExpense Claim Detail` child, `tabExpense Claim` par + SET child.cost_center = par.cost_center + WHERE child.parent = par.name + """) \ No newline at end of file diff --git a/erpnext/patches/v12_0/set_cwip_and_delete_asset_settings.py b/erpnext/patches/v12_0/set_cwip_and_delete_asset_settings.py new file mode 100644 index 00000000000..4d4fc7c4629 --- /dev/null +++ b/erpnext/patches/v12_0/set_cwip_and_delete_asset_settings.py @@ -0,0 +1,17 @@ +from __future__ import unicode_literals +import frappe +from frappe.utils import cint + + +def execute(): + '''Get 'Disable CWIP Accounting value' from Asset Settings, set it in 'Enable Capital Work in Progress Accounting' field + in Company, delete Asset Settings ''' + + if frappe.db.exists("DocType", "Asset Settings"): + frappe.reload_doctype("Asset Category") + cwip_value = frappe.db.get_single_value("Asset Settings", "disable_cwip_accounting") + + frappe.db.sql("""UPDATE `tabAsset Category` SET enable_cwip_accounting = %s""", cint(cwip_value)) + + frappe.db.sql("""DELETE FROM `tabSingles` where doctype = 'Asset Settings'""") + frappe.delete_doc_if_exists("DocType", "Asset Settings") \ No newline at end of file diff --git a/erpnext/patches/v12_0/set_employee_preferred_emails.py b/erpnext/patches/v12_0/set_employee_preferred_emails.py new file mode 100644 index 00000000000..f6eb12e2b9f --- /dev/null +++ b/erpnext/patches/v12_0/set_employee_preferred_emails.py @@ -0,0 +1,16 @@ +import frappe + + +def execute(): + employees = frappe.get_all("Employee", + filters={"prefered_email": ""}, + fields=["name", "prefered_contact_email", "company_email", "personal_email", "user_id"]) + + for employee in employees: + if not employee.prefered_contact_email: + continue + + preferred_email_field = frappe.scrub(employee.prefered_contact_email) + + preferred_email = employee.get(preferred_email_field) + frappe.db.set_value("Employee", employee.name, "prefered_email", preferred_email, update_modified=False) diff --git a/erpnext/patches/v12_0/set_gst_category.py b/erpnext/patches/v12_0/set_gst_category.py index 54bc5b3c74f..55bbdee7edf 100644 --- a/erpnext/patches/v12_0/set_gst_category.py +++ b/erpnext/patches/v12_0/set_gst_category.py @@ -7,6 +7,8 @@ def execute(): if not company: return + frappe.reload_doc('accounts', 'doctype', 'Tax Category') + make_custom_fields() for doctype in ['Sales Invoice', 'Purchase Invoice']: diff --git a/erpnext/patches/v12_0/set_job_offer_applicant_email.py b/erpnext/patches/v12_0/set_job_offer_applicant_email.py new file mode 100644 index 00000000000..7dd8492081e --- /dev/null +++ b/erpnext/patches/v12_0/set_job_offer_applicant_email.py @@ -0,0 +1,12 @@ +import frappe + + +def execute(): + frappe.reload_doc("hr", "doctype", "job_offer") + + frappe.db.sql(""" + UPDATE + `tabJob Offer` AS offer + SET + applicant_email = (SELECT email_id FROM `tabJob Applicant` WHERE name = offer.job_applicant) + """) diff --git a/erpnext/patches/v12_0/set_lead_title_field.py b/erpnext/patches/v12_0/set_lead_title_field.py new file mode 100644 index 00000000000..86e00038f6c --- /dev/null +++ b/erpnext/patches/v12_0/set_lead_title_field.py @@ -0,0 +1,11 @@ +import frappe + + +def execute(): + frappe.reload_doc("crm", "doctype", "lead") + frappe.db.sql(""" + UPDATE + `tabLead` + SET + title = IF(organization_lead = 1, company_name, lead_name) + """) diff --git a/erpnext/patches/v12_0/set_permission_einvoicing.py b/erpnext/patches/v12_0/set_permission_einvoicing.py new file mode 100644 index 00000000000..1095c8c43e7 --- /dev/null +++ b/erpnext/patches/v12_0/set_permission_einvoicing.py @@ -0,0 +1,15 @@ +import frappe +from erpnext.regional.italy.setup import make_custom_fields +from frappe.permissions import add_permission, update_permission_property + +def execute(): + company = frappe.get_all('Company', filters = {'country': 'Italy'}) + + if not company: + return + + make_custom_fields() + + add_permission('Import Supplier Invoice', 'Accounts Manager', 0) + update_permission_property('Import Supplier Invoice', 'Accounts Manager', 0, 'write', 1) + update_permission_property('Import Supplier Invoice', 'Accounts Manager', 0, 'create', 1) \ No newline at end of file diff --git a/erpnext/patches/v12_0/set_produced_qty_field_in_sales_order_for_work_order.py b/erpnext/patches/v12_0/set_produced_qty_field_in_sales_order_for_work_order.py index 44d8fa767af..07026732fd4 100644 --- a/erpnext/patches/v12_0/set_produced_qty_field_in_sales_order_for_work_order.py +++ b/erpnext/patches/v12_0/set_produced_qty_field_in_sales_order_for_work_order.py @@ -3,8 +3,12 @@ from frappe.utils import flt from erpnext.selling.doctype.sales_order.sales_order import update_produced_qty_in_so_item def execute(): - frappe.reload_doctype('Sales Order Item') - frappe.reload_doctype('Sales Order') - sales_order_items = frappe.db.get_all('Sales Order Item', ['name']) - for so_item in sales_order_items: - update_produced_qty_in_so_item(so_item.get('name')) \ No newline at end of file + frappe.reload_doctype('Sales Order Item') + frappe.reload_doctype('Sales Order') + + for d in frappe.get_all('Work Order', + fields = ['sales_order', 'sales_order_item'], + filters={'sales_order': ('!=', ''), 'sales_order_item': ('!=', '')}): + + # update produced qty in sales order + update_produced_qty_in_so_item(d.sales_order, d.sales_order_item) \ No newline at end of file diff --git a/erpnext/patches/v12_0/set_production_capacity_in_workstation.py b/erpnext/patches/v12_0/set_production_capacity_in_workstation.py new file mode 100644 index 00000000000..bae1e28deb9 --- /dev/null +++ b/erpnext/patches/v12_0/set_production_capacity_in_workstation.py @@ -0,0 +1,8 @@ +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.reload_doc("manufacturing", "doctype", "workstation") + + frappe.db.sql(""" UPDATE `tabWorkstation` + SET production_capacity = 1 """) \ No newline at end of file diff --git a/erpnext/patches/v12_0/set_published_in_hub_tracked_item.py b/erpnext/patches/v12_0/set_published_in_hub_tracked_item.py new file mode 100644 index 00000000000..e54c7f315c6 --- /dev/null +++ b/erpnext/patches/v12_0/set_published_in_hub_tracked_item.py @@ -0,0 +1,12 @@ +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.reload_doc("Hub Node", "doctype", "Hub Tracked Item") + if not frappe.db.a_row_exists("Hub Tracked Item"): + return + + frappe.db.sql(''' + Update `tabHub Tracked Item` + SET published = 1 + ''') diff --git a/erpnext/patches/v12_0/update_gst_category.py b/erpnext/patches/v12_0/update_gst_category.py new file mode 100644 index 00000000000..963edad150e --- /dev/null +++ b/erpnext/patches/v12_0/update_gst_category.py @@ -0,0 +1,19 @@ +from __future__ import unicode_literals +import frappe + +def execute(): + + company = frappe.get_all('Company', filters = {'country': 'India'}) + if not company: + return + + frappe.db.sql(""" UPDATE `tabSales Invoice` set gst_category = 'Unregistered' + where gst_category = 'Registered Regular' + and ifnull(customer_gstin, '')='' + and ifnull(billing_address_gstin,'')='' + """) + + frappe.db.sql(""" UPDATE `tabPurchase Invoice` set gst_category = 'Unregistered' + where gst_category = 'Registered Regular' + and ifnull(supplier_gstin, '')='' + """) \ No newline at end of file diff --git a/erpnext/patches/v12_0/update_price_or_product_discount.py b/erpnext/patches/v12_0/update_price_or_product_discount.py new file mode 100644 index 00000000000..3a8cd43e302 --- /dev/null +++ b/erpnext/patches/v12_0/update_price_or_product_discount.py @@ -0,0 +1,8 @@ +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.reload_doc("accounts", "doctype", "pricing_rule") + + frappe.db.sql(""" UPDATE `tabPricing Rule` SET price_or_product_discount = 'Price' + WHERE ifnull(price_or_product_discount,'') = '' """) diff --git a/erpnext/patches/v8_7/set_offline_in_pos_settings.py b/erpnext/patches/v8_7/set_offline_in_pos_settings.py index b24fe37a285..7d2882e0641 100644 --- a/erpnext/patches/v8_7/set_offline_in_pos_settings.py +++ b/erpnext/patches/v8_7/set_offline_in_pos_settings.py @@ -5,6 +5,7 @@ from __future__ import unicode_literals import frappe def execute(): + frappe.reload_doc('accounts', 'doctype', 'pos_field') frappe.reload_doc('accounts', 'doctype', 'pos_settings') doc = frappe.get_doc('POS Settings') diff --git a/erpnext/patches/v8_7/sync_india_custom_fields.py b/erpnext/patches/v8_7/sync_india_custom_fields.py index 80d66c2ab19..73e1b182e82 100644 --- a/erpnext/patches/v8_7/sync_india_custom_fields.py +++ b/erpnext/patches/v8_7/sync_india_custom_fields.py @@ -13,6 +13,8 @@ def execute(): frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_declaration_category') frappe.reload_doc('hr', 'doctype', 'employee_tax_exemption_proof_submission_detail') + frappe.reload_doc('accounts', 'doctype', 'tax_category') + for doctype in ["Sales Invoice", "Delivery Note", "Purchase Invoice"]: frappe.db.sql("""delete from `tabCustom Field` where dt = %s and fieldname in ('port_code', 'shipping_bill_number', 'shipping_bill_date')""", doctype) @@ -29,4 +31,4 @@ def execute(): update tabAddress set gst_state_number=concat("0", gst_state_number) where ifnull(gst_state_number, '') != '' and gst_state_number<10 - """) \ No newline at end of file + """) diff --git a/erpnext/patches/v8_9/add_setup_progress_actions.py b/erpnext/patches/v8_9/add_setup_progress_actions.py index fe123111bce..77501073cf4 100644 --- a/erpnext/patches/v8_9/add_setup_progress_actions.py +++ b/erpnext/patches/v8_9/add_setup_progress_actions.py @@ -5,6 +5,9 @@ from frappe import _ def execute(): """Add setup progress actions""" + if not frappe.db.exists('DocType', 'Setup Progress') or not frappe.db.exists('DocType', 'Setup Progress Action'): + return + frappe.reload_doc("setup", "doctype", "setup_progress") frappe.reload_doc("setup", "doctype", "setup_progress_action") diff --git a/erpnext/patches/v9_2/rename_translated_domains_in_en.py b/erpnext/patches/v9_2/rename_translated_domains_in_en.py index aec5d438ec8..e5a9e2461fa 100644 --- a/erpnext/patches/v9_2/rename_translated_domains_in_en.py +++ b/erpnext/patches/v9_2/rename_translated_domains_in_en.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals import frappe from frappe import _ +from frappe.model.rename_doc import rename_doc def execute(): frappe.reload_doc('stock', 'doctype', 'item') @@ -20,11 +21,11 @@ def execute(): if frappe.db.exists("Domain", domain): merge=True - frappe.rename_doc("Domain", translated_domain, domain, ignore_permissions=True, merge=merge) + rename_doc("Domain", translated_domain, domain, ignore_permissions=True, merge=merge) domain_settings = frappe.get_single("Domain Settings") active_domains = [d.domain for d in domain_settings.active_domains] - + try: for domain in active_domains: domain = frappe.get_doc("Domain", domain) diff --git a/erpnext/portal/doctype/homepage/test_homepage.py b/erpnext/portal/doctype/homepage/test_homepage.py index b262c4640cc..bf5c4025a0b 100644 --- a/erpnext/portal/doctype/homepage/test_homepage.py +++ b/erpnext/portal/doctype/homepage/test_homepage.py @@ -5,7 +5,7 @@ from __future__ import unicode_literals import frappe import unittest -from frappe.tests.test_website import set_request +from frappe.utils import set_request from frappe.website.render import render class TestHomepage(unittest.TestCase): diff --git a/erpnext/portal/doctype/homepage_section/test_homepage_section.py b/erpnext/portal/doctype/homepage_section/test_homepage_section.py index c52b7a96c65..5b3196def25 100644 --- a/erpnext/portal/doctype/homepage_section/test_homepage_section.py +++ b/erpnext/portal/doctype/homepage_section/test_homepage_section.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe import unittest from bs4 import BeautifulSoup -from frappe.tests.test_website import set_request +from frappe.utils import set_request from frappe.website.render import render class TestHomepageSection(unittest.TestCase): diff --git a/erpnext/portal/product_configurator/test_product_configurator.py b/erpnext/portal/product_configurator/test_product_configurator.py index a534e5f8382..97042dba92c 100644 --- a/erpnext/portal/product_configurator/test_product_configurator.py +++ b/erpnext/portal/product_configurator/test_product_configurator.py @@ -2,7 +2,7 @@ from __future__ import unicode_literals from bs4 import BeautifulSoup import frappe, unittest -from frappe.tests.test_website import set_request, get_html_for_route +from frappe.utils import set_request, get_html_for_route from frappe.website.render import render from erpnext.portal.product_configurator.utils import get_products_for_website from erpnext.stock.doctype.item.test_item import make_item_variant diff --git a/erpnext/portal/product_configurator/utils.py b/erpnext/portal/product_configurator/utils.py index 61c50e5fe01..0993e69e042 100644 --- a/erpnext/portal/product_configurator/utils.py +++ b/erpnext/portal/product_configurator/utils.py @@ -52,7 +52,6 @@ def get_attribute_filter_data(): def get_products_for_website(field_filters=None, attribute_filters=None, search=None): - if attribute_filters: item_codes = get_item_codes_by_attributes(attribute_filters) items_by_attributes = get_items([['name', 'in', item_codes]]) @@ -302,6 +301,8 @@ def get_items(filters=None, search=None): if isinstance(filters, dict): filters = [['Item', fieldname, '=', value] for fieldname, value in filters.items()] + enabled_items_filter = get_conditions({ 'disabled': 0 }, 'and') + show_in_website_condition = '' if products_settings.hide_variants: show_in_website_condition = get_conditions({'show_in_website': 1 }, 'and') @@ -313,19 +314,32 @@ def get_items(filters=None, search=None): search_condition = '' if search: + # Default fields to search from + default_fields = {'name', 'item_name', 'description', 'item_group'} + + # Get meta search fields + meta = frappe.get_meta("Item") + meta_fields = set(meta.get_search_fields()) + + # Join the meta fields and default fields set + search_fields = default_fields.union(meta_fields) + try: + if frappe.db.count('Item', cache=True) > 50000: + search_fields.remove('description') + except KeyError: + pass + + # Build or filters for query search = '%{}%'.format(search) - or_filters = [ - ['name', 'like', search], - ['item_name', 'like', search], - ['description', 'like', search], - ['item_group', 'like', search] - ] + or_filters = [[field, 'like', search] for field in search_fields] + search_condition = get_conditions(or_filters, 'or') filter_condition = get_conditions(filters, 'and') where_conditions = ' and '.join( - [condition for condition in [show_in_website_condition, search_condition, filter_condition] if condition] + [condition for condition in [enabled_items_filter, show_in_website_condition, \ + search_condition, filter_condition] if condition] ) left_joins = [] diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index 4a03a58f80a..3570a0f2be4 100644 --- a/erpnext/projects/doctype/project/project.js +++ b/erpnext/projects/doctype/project/project.js @@ -4,20 +4,16 @@ frappe.ui.form.on("Project", { setup(frm) { frm.make_methods = { 'Timesheet': () => { - let doctype = 'Timesheet'; - frappe.model.with_doctype(doctype, () => { - let new_doc = frappe.model.get_new_doc(doctype); - - // add a new row and set the project - let time_log = frappe.model.get_new_doc('Timesheet Detail'); - time_log.project = frm.doc.name; - time_log.parent = new_doc.name; - time_log.parentfield = 'time_logs'; - time_log.parenttype = 'Timesheet'; - new_doc.time_logs = [time_log]; - - frappe.ui.form.make_quick_entry(doctype, null, null, new_doc); - }); + open_form(frm, "Timesheet", "Timesheet Detail", "time_logs"); + }, + 'Purchase Order': () => { + open_form(frm, "Purchase Order", "Purchase Order Item", "items"); + }, + 'Purchase Receipt': () => { + open_form(frm, "Purchase Receipt", "Purchase Receipt Item", "items"); + }, + 'Purchase Invoice': () => { + open_form(frm, "Purchase Invoice", "Purchase Invoice Item", "items"); }, }; }, @@ -53,23 +49,6 @@ frappe.ui.form.on("Project", { filters: filters }; }); - - if (frappe.model.can_read("Task")) { - frm.add_custom_button(__("Gantt Chart"), function () { - frappe.route_options = { - "project": frm.doc.name - }; - frappe.set_route("List", "Task", "Gantt"); - }); - - frm.add_custom_button(__("Kanban Board"), () => { - frappe.call('erpnext.projects.doctype.project.project.create_kanban_board_if_not_exists', { - project: frm.doc.project_name - }).then(() => { - frappe.set_route('List', 'Task', 'Kanban', frm.doc.project_name); - }); - }); - } }, refresh: function (frm) { @@ -97,6 +76,23 @@ frappe.ui.form.on("Project", { frm.events.set_status(frm, 'Cancelled'); }, __('Set Status')); } + + if (frappe.model.can_read("Task")) { + frm.add_custom_button(__("Gantt Chart"), function () { + frappe.route_options = { + "project": frm.doc.name + }; + frappe.set_route("List", "Task", "Gantt"); + }); + + frm.add_custom_button(__("Kanban Board"), () => { + frappe.call('erpnext.projects.doctype.project.project.create_kanban_board_if_not_exists', { + project: frm.doc.project_name + }).then(() => { + frappe.set_route('List', 'Task', 'Kanban', frm.doc.project_name); + }); + }); + } }, create_duplicate: function(frm) { @@ -123,3 +119,20 @@ frappe.ui.form.on("Project", { }, }); + +function open_form(frm, doctype, child_doctype, parentfield) { + frappe.model.with_doctype(doctype, () => { + let new_doc = frappe.model.get_new_doc(doctype); + + // add a new row and set the project + let new_child_doc = frappe.model.get_new_doc(child_doctype); + new_child_doc.project = frm.doc.name; + new_child_doc.parent = new_doc.name; + new_child_doc.parentfield = parentfield; + new_child_doc.parenttype = doctype; + new_doc[parentfield] = [new_child_doc]; + + frappe.ui.form.make_quick_entry(doctype, null, null, new_doc); + }); + +} \ No newline at end of file diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py index bf6e21aa4d8..afdb5b7a01d 100644 --- a/erpnext/projects/doctype/project/project.py +++ b/erpnext/projects/doctype/project/project.py @@ -188,7 +188,7 @@ class Project(Document): def send_welcome_email(self): url = get_url("/project/?name={0}".format(self.name)) messages = ( - _("You have been invited to collaborate on the project: {0}".format(self.name)), + _("You have been invited to collaborate on the project: {0}").format(self.name), url, _("Join") ) diff --git a/erpnext/projects/doctype/task/task.py b/erpnext/projects/doctype/task/task.py index 90e9f05f225..d56bb238165 100755 --- a/erpnext/projects/doctype/task/task.py +++ b/erpnext/projects/doctype/task/task.py @@ -7,9 +7,10 @@ import json import frappe from frappe import _, throw -from frappe.utils import add_days, cstr, date_diff, get_link_to_form, getdate +from frappe.utils import add_days, cstr, date_diff, get_link_to_form, getdate, today from frappe.utils.nestedset import NestedSet from frappe.desk.form.assign_to import close_all_assignments, clear +from frappe.utils import date_diff class CircularReferenceError(frappe.ValidationError): pass class EndDateCannotBeGreaterThanProjectEndDateError(frappe.ValidationError): pass @@ -28,22 +29,35 @@ class Task(NestedSet): def validate(self): self.validate_dates() + self.validate_parent_project_dates() self.validate_progress() self.validate_status() self.update_depends_on() def validate_dates(self): if self.exp_start_date and self.exp_end_date and getdate(self.exp_start_date) > getdate(self.exp_end_date): - frappe.throw(_("'Expected Start Date' can not be greater than 'Expected End Date'")) + frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Expected Start Date"), \ + frappe.bold("Expected End Date"))) if self.act_start_date and self.act_end_date and getdate(self.act_start_date) > getdate(self.act_end_date): - frappe.throw(_("'Actual Start Date' can not be greater than 'Actual End Date'")) + frappe.throw(_("{0} can not be greater than {1}").format(frappe.bold("Actual Start Date"), \ + frappe.bold("Actual End Date"))) + + def validate_parent_project_dates(self): + if not self.project or frappe.flags.in_test: + return + + expected_end_date = frappe.db.get_value("Project", self.project, "expected_end_date") + + if expected_end_date: + validate_project_dates(getdate(expected_end_date), self, "exp_start_date", "exp_end_date", "Expected") + validate_project_dates(getdate(expected_end_date), self, "act_start_date", "act_end_date", "Actual") def validate_status(self): if self.status!=self.get_db_value("status") and self.status == "Completed": for d in self.depends_on: - if frappe.db.get_value("Task", d.task, "status") != "Completed": - frappe.throw(_("Cannot close task {0} as its dependant task {1} is not closed.").format(frappe.bold(self.name), frappe.bold(d.task))) + if frappe.db.get_value("Task", d.task, "status") not in ("Completed", "Cancelled"): + frappe.throw(_("Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.").format(frappe.bold(self.name), frappe.bold(d.task))) close_all_assignments(self.doctype, self.name) @@ -198,8 +212,11 @@ def set_multiple_status(names, status): task.save() def set_tasks_as_overdue(): - tasks = frappe.get_all("Task", filters={'status':['not in',['Cancelled', 'Completed']]}) + tasks = frappe.get_all("Task", filters={'status':['not in',['Cancelled', 'Closed']]}) for task in tasks: + if frappe.db.get_value("Task", task.name, "status") in 'Pending Review': + if getdate(frappe.db.get_value("Task", task.name, "review_date")) < getdate(today()): + continue frappe.get_doc("Task", task.name).update_status() @frappe.whitelist() @@ -255,3 +272,10 @@ def add_multiple_tasks(data, parent): def on_doctype_update(): frappe.db.add_index("Task", ["lft", "rgt"]) + +def validate_project_dates(project_end_date, task, task_start, task_end, actual_or_expected_date): + if task.get(task_start) and date_diff(project_end_date, getdate(task.get(task_start))) < 0: + frappe.throw(_("Task's {0} Start Date cannot be after Project's End Date.").format(actual_or_expected_date)) + + if task.get(task_end) and date_diff(project_end_date, getdate(task.get(task_end))) < 0: + frappe.throw(_("Task's {0} End Date cannot be after Project's End Date.").format(actual_or_expected_date)) diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index bc88250c8aa..e90821689bd 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -188,6 +188,9 @@ class Timesheet(Document): }, as_dict=True) # check internal overlap for time_log in self.time_logs: + if not (time_log.from_time and time_log.to_time + and args.from_time and args.to_time): continue + if (fieldname != 'workstation' or args.get(fieldname) == time_log.get(fieldname)) and \ args.idx != time_log.idx and ((args.from_time > time_log.from_time and args.from_time < time_log.to_time) or (args.to_time > time_log.from_time and args.to_time < time_log.to_time) or diff --git a/erpnext/public/js/conf.js b/erpnext/public/js/conf.js index 095e744926b..0f88f133501 100644 --- a/erpnext/public/js/conf.js +++ b/erpnext/public/js/conf.js @@ -50,7 +50,7 @@ $.extend(frappe.breadcrumbs.preferred, { "Territory": "Selling", "Sales Person": "Selling", "Sales Partner": "Selling", - "Brand": "Selling" + "Brand": "Stock" }); $.extend(frappe.breadcrumbs.module_map, { diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index 3dfc8911fc4..f4eaad58dae 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -64,7 +64,7 @@ frappe.ui.form.on(cur_frm.doctype, { } }) } - } + } }); frappe.ui.form.on('Sales Invoice Payment', { @@ -355,4 +355,4 @@ cur_frm.pformat.taxes= function(doc){ out += '

'; } return out; -} +} \ No newline at end of file diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index 02c30587f67..3d4c4a64595 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -30,7 +30,7 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ && frappe.meta.has_field(this.frm.doc.doctype, "disable_rounded_total")) { var df = frappe.meta.get_docfield(this.frm.doc.doctype, "disable_rounded_total"); - var disable = df.default || cint(frappe.sys_defaults.disable_rounded_total); + var disable = cint(df.default) || cint(frappe.sys_defaults.disable_rounded_total); this.frm.set_value("disable_rounded_total", disable); } @@ -107,6 +107,12 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({ filters:{ 'item_code': row.item_code } } }); + + if(this.frm.fields_dict["items"].grid.get_field('item_code')) { + this.frm.set_query("item_tax_template", "items", function(doc, cdt, cdn) { + return me.set_query_for_item_tax_template(doc, cdt, cdn) + }); + } }, refresh: function(doc) { diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 2ece7110406..28fb6490254 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -388,9 +388,14 @@ erpnext.taxes_and_totals = erpnext.payments.extend({ var diff = me.frm.doc.total + non_inclusive_tax_amount - flt(last_tax.total, precision("grand_total")); + if(me.discount_amount_applied && me.frm.doc.discount_amount) { + diff -= flt(me.frm.doc.discount_amount); + } + + diff = flt(diff, precision("rounding_adjustment")); + if ( diff && Math.abs(diff) <= (5.0 / Math.pow(10, precision("tax_amount", last_tax))) ) { - this.frm.doc.rounding_adjustment = flt(flt(this.frm.doc.rounding_adjustment) + diff, - precision("rounding_adjustment")); + me.frm.doc.rounding_adjustment = diff; } } } diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index ca492baf5ab..51ab48a3ab5 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -500,6 +500,9 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ () => { var d = locals[cdt][cdn]; me.add_taxes_from_item_tax_template(d.item_tax_rate); + if (d.free_item_data) { + me.apply_product_discount(d.free_item_data); + } }, () => me.frm.script_manager.trigger("price_list_rate", cdt, cdn), () => me.toggle_conversion_factor(item), @@ -516,7 +519,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } }, () => me.conversion_factor(doc, cdt, cdn, true), - () => me.validate_pricing_rule(item) + () => me.remove_pricing_rule(item) ]); } } @@ -1174,7 +1177,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ callback: function(r) { if (!r.exc && r.message) { r.message.forEach(row_item => { - me.validate_pricing_rule(row_item); + me.remove_pricing_rule(row_item); }); me._set_values_for_item_list(r.message); me.calculate_taxes_and_totals(); @@ -1283,6 +1286,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ _set_values_for_item_list: function(children) { var me = this; var price_list_rate_changed = false; + var items_rule_dict = {}; + for(var i=0, l=children.length; i { + if (in_list(data.apply_rule_on_other_items, d[data.apply_rule_on])) { + for(var k in data) { + if (in_list(fields, k)) { + frappe.model.set_value(d.doctype, d.name, k, data[k]); + } + } + } + }); + } + }, + + apply_product_discount: function(free_item_data) { + const items = this.frm.doc.items.filter(d => (d.item_code == free_item_data.item_code + && d.is_free_item)) || []; + + if (!items.length) { + let row_to_modify = frappe.model.add_child(this.frm.doc, + this.frm.doc.doctype + ' Item', 'items'); + + for (let key in free_item_data) { + row_to_modify[key] = free_item_data[key]; + } + } + }, + apply_price_list: function(item, reset_plc_conversion) { // We need to reset plc_conversion_rate sometimes because the call to // `erpnext.stock.get_item_details.apply_price_list` is sensitive to its value @@ -1348,33 +1396,12 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ }); }, - validate_pricing_rule: function(item) { + remove_pricing_rule: function(item) { let me = this; - const fields = ["discount_percentage", "discount_amount", "pricing_rules"]; + const fields = ["discount_percentage", + "discount_amount", "margin_rate_or_amount", "rate_with_margin"]; - if (item.pricing_rules) { - frappe.call({ - method: "erpnext.accounts.doctype.pricing_rule.utils.validate_pricing_rule_for_different_cond", - args: { - doc: me.frm.doc - }, - callback: function(r) { - if (r.message) { - r.message.items.forEach(d => { - me.frm.doc.items.forEach(row => { - if(d.name == row.name) { - fields.forEach(f => { - row[f] = d[f]; - }); - } - }); - }); - - me.trigger_price_list_rate(); - } - } - }); - } else if(item.remove_free_item) { + if(item.remove_free_item) { var items = []; me.frm.doc.items.forEach(d => { @@ -1392,6 +1419,12 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ fields.forEach(f => { row[f] = 0; }); + + ["pricing_rules", "margin_type"].forEach(field => { + if (row[field]) { + row[field] = ''; + } + }) } }); @@ -1667,6 +1700,29 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } }, + set_query_for_item_tax_template: function(doc, cdt, cdn) { + + var item = frappe.get_doc(cdt, cdn); + if(!item.item_code) { + frappe.throw(__("Please enter Item Code to get item taxes")); + } else { + + let filters = { + 'item_code': item.item_code, + 'valid_from': doc.transaction_date || doc.bill_date || doc.posting_date, + 'item_group': item.item_group, + } + + if (doc.tax_category) + filters['tax_category'] = doc.tax_category; + + return { + query: "erpnext.controllers.queries.get_tax_template", + filters: filters + } + } + }, + payment_terms_template: function() { var me = this; const doc = this.frm.doc; @@ -1711,6 +1767,14 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } }, + against_blanket_order: function(doc, cdt, cdn) { + var item = locals[cdt][cdn]; + if(!item.against_blanket_order) { + frappe.model.set_value(this.frm.doctype + " Item", item.name, "blanket_order", null); + frappe.model.set_value(this.frm.doctype + " Item", item.name, "blanket_order_rate", 0.00); + } + }, + blanket_order: function(doc, cdt, cdn) { var me = this; var item = locals[cdt][cdn]; @@ -1741,14 +1805,28 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } }, + set_reserve_warehouse: function() { + this.autofill_warehouse("reserve_warehouse"); + }, + set_warehouse: function() { + this.autofill_warehouse("warehouse"); + }, + + autofill_warehouse : function (warehouse_field) { + // set warehouse in all child table rows var me = this; - if(this.frm.doc.set_warehouse) { - $.each(this.frm.doc.items || [], function(i, item) { - frappe.model.set_value(me.frm.doctype + " Item", item.name, "warehouse", me.frm.doc.set_warehouse); + let warehouse = (warehouse_field === "warehouse") ? me.frm.doc.set_warehouse : me.frm.doc.set_reserve_warehouse; + let child_table = (warehouse_field === "warehouse") ? me.frm.doc.items : me.frm.doc.supplied_items; + let doctype = (warehouse_field === "warehouse") ? (me.frm.doctype + " Item") : (me.frm.doctype + " Item Supplied"); + + if(warehouse) { + $.each(child_table || [], function(i, item) { + frappe.model.set_value(doctype, item.name, warehouse_field, warehouse); }); } }, + coupon_code: function() { var me = this; frappe.run_serially([ @@ -1760,14 +1838,44 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({ } }); -erpnext.show_serial_batch_selector = function(frm, d, callback, on_close, show_dialog) { +erpnext.show_serial_batch_selector = function (frm, d, callback, on_close, show_dialog) { + let warehouse, receiving_stock, existing_stock; + if (frm.doc.is_return) { + if (["Purchase Receipt", "Purchase Invoice"].includes(frm.doc.doctype)) { + existing_stock = true; + warehouse = d.warehouse; + } else if (["Delivery Note", "Sales Invoice"].includes(frm.doc.doctype)) { + receiving_stock = true; + } + } else { + if (frm.doc.doctype == "Stock Entry") { + if (frm.doc.purpose == "Material Receipt") { + receiving_stock = true; + } else { + existing_stock = true; + warehouse = d.s_warehouse; + } + } else { + existing_stock = true; + warehouse = d.warehouse; + } + } + + if (!warehouse) { + if (receiving_stock) { + warehouse = ["like", ""]; + } else if (existing_stock) { + warehouse = ["!=", ""]; + } + } + frappe.require("assets/erpnext/js/utils/serial_no_batch_selector.js", function() { new erpnext.SerialNoBatchSelector({ frm: frm, item: d, warehouse_details: { type: "Warehouse", - name: d.warehouse + name: warehouse }, callback: callback, on_close: on_close diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 63e057c39d1..dead309a5d0 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -4,7 +4,7 @@ erpnext.financial_statements = { "filters": get_filters(), "formatter": function(value, row, column, data, default_formatter) { if (column.fieldname=="account") { - value = data.account_name; + value = data.account_name || value; column.link_onclick = "erpnext.financial_statements.open_general_ledger(" + JSON.stringify(data) + ")"; diff --git a/erpnext/public/js/hub/PageContainer.vue b/erpnext/public/js/hub/PageContainer.vue index f151add8d5a..54c359766d3 100644 --- a/erpnext/public/js/hub/PageContainer.vue +++ b/erpnext/public/js/hub/PageContainer.vue @@ -24,7 +24,7 @@ import NotFound from './pages/NotFound.vue'; function get_route_map() { const read_only_routes = { 'marketplace/home': Home, - 'marketplace/search/:keyword': Search, + 'marketplace/search/:category/:keyword': Search, 'marketplace/category/:category': Category, 'marketplace/item/:item': Item, 'marketplace/seller/:seller': Seller, diff --git a/erpnext/public/js/hub/components/edit_details_dialog.js b/erpnext/public/js/hub/components/edit_details_dialog.js new file mode 100644 index 00000000000..97c5f83a13a --- /dev/null +++ b/erpnext/public/js/hub/components/edit_details_dialog.js @@ -0,0 +1,41 @@ +function edit_details_dialog(params) { + let dialog = new frappe.ui.Dialog({ + title: __('Update Details'), + fields: [ + { + label: 'Item Name', + fieldname: 'item_name', + fieldtype: 'Data', + default: params.defaults.item_name, + reqd: 1 + }, + { + label: 'Hub Category', + fieldname: 'hub_category', + fieldtype: 'Autocomplete', + default: params.defaults.hub_category, + options: [], + reqd: 1 + }, + { + label: 'Description', + fieldname: 'description', + fieldtype: 'Text', + default: params.defaults.description, + options: [], + reqd: 1 + } + ], + primary_action_label: params.primary_action.label || __('Update Details'), + primary_action: params.primary_action.fn + }); + + hub.call('get_categories').then(categories => { + categories = categories.map(d => d.name); + dialog.fields_dict.hub_category.set_data(categories); + }); + + return dialog; +} + +export { edit_details_dialog }; diff --git a/erpnext/public/js/hub/components/item_publish_dialog.js b/erpnext/public/js/hub/components/item_publish_dialog.js index e49ba539185..08de5b30b6c 100644 --- a/erpnext/public/js/hub/components/item_publish_dialog.js +++ b/erpnext/public/js/hub/components/item_publish_dialog.js @@ -3,24 +3,24 @@ function ItemPublishDialog(primary_action, secondary_action) { title: __('Edit Publishing Details'), fields: [ { - "label": "Item Code", - "fieldname": "item_code", - "fieldtype": "Data", - "read_only": 1 + label: __('Item Code'), + fieldname: 'item_code', + fieldtype: 'Data', + read_only: 1 }, { - "label": "Hub Category", - "fieldname": "hub_category", - "fieldtype": "Autocomplete", - "options": [], - "reqd": 1 + label: __('Hub Category'), + fieldname: 'hub_category', + fieldtype: 'Autocomplete', + options: [], + reqd: 1 }, { - "label": "Images", - "fieldname": "image_list", - "fieldtype": "MultiSelect", - "options": [], - "reqd": 1 + label: __('Images'), + fieldname: 'image_list', + fieldtype: 'MultiSelect', + options: [], + reqd: 1 } ], primary_action_label: primary_action.label || __('Set Details'), @@ -28,15 +28,12 @@ function ItemPublishDialog(primary_action, secondary_action) { secondary_action: secondary_action.fn }); - hub.call('get_categories') - .then(categories => { - categories = categories.map(d => d.name); - dialog.fields_dict.hub_category.set_data(categories); - }); + hub.call('get_categories').then(categories => { + categories = categories.map(d => d.name); + dialog.fields_dict.hub_category.set_data(categories); + }); return dialog; } -export { - ItemPublishDialog -} +export { ItemPublishDialog }; diff --git a/erpnext/public/js/hub/pages/Category.vue b/erpnext/public/js/hub/pages/Category.vue index 3a0e6bfab83..057fe8bc617 100644 --- a/erpnext/public/js/hub/pages/Category.vue +++ b/erpnext/public/js/hub/pages/Category.vue @@ -3,6 +3,12 @@ class="marketplace-page" :data-page-name="page_name" > + +
{{ page_title }}
diff --git a/erpnext/public/js/hub/pages/Home.vue b/erpnext/public/js/hub/pages/Home.vue index 353656957df..8fe824566db 100644 --- a/erpnext/public/js/hub/pages/Home.vue +++ b/erpnext/public/js/hub/pages/Home.vue @@ -58,6 +58,13 @@ export default { this.search_value = ''; this.get_items(); }, + mounted() { + frappe.route.on('change', () => { + if (frappe.get_route_str() === 'marketplace/home') { + this.get_items(); + } + }) + }, methods: { get_items() { hub.call('get_data_for_homepage', frappe.defaults ? { @@ -98,7 +105,7 @@ export default { }, set_search_route() { - frappe.set_route('marketplace', 'search', this.search_value); + frappe.set_route('marketplace', 'search', 'All', this.search_value); }, } } diff --git a/erpnext/public/js/hub/pages/Item.vue b/erpnext/public/js/hub/pages/Item.vue index 841d0046db8..51ade42cbae 100644 --- a/erpnext/public/js/hub/pages/Item.vue +++ b/erpnext/public/js/hub/pages/Item.vue @@ -1,10 +1,5 @@