diff --git a/erpnext/accounts/doctype/account/account.js b/erpnext/accounts/doctype/account/account.js
index bb059f673c..f7f1a5fb15 100644
--- a/erpnext/accounts/doctype/account/account.js
+++ b/erpnext/accounts/doctype/account/account.js
@@ -42,15 +42,14 @@ frappe.ui.form.on('Account', {
// show / hide convert buttons
frm.trigger('add_toolbar_buttons');
}
- frm.add_custom_button(__('Update Account Name / Number'), function () {
- frm.trigger("update_account_number");
- });
- }
-
- if(!frm.doc.__islocal) {
- frm.add_custom_button(__('Merge Account'), function () {
- frm.trigger("merge_account");
- });
+ if (frm.has_perm('write')) {
+ frm.add_custom_button(__('Update Account Name / Number'), function () {
+ frm.trigger("update_account_number");
+ });
+ frm.add_custom_button(__('Merge Account'), function () {
+ frm.trigger("merge_account");
+ });
+ }
}
},
account_type: function (frm) {
diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py
index ecf67dd1db..68efe37719 100644
--- a/erpnext/accounts/doctype/account/account.py
+++ b/erpnext/accounts/doctype/account/account.py
@@ -268,7 +268,7 @@ def update_account_number(name, account_name, account_number=None):
new_name = get_account_autoname(account_number, account_name, account.company)
if name != new_name:
- frappe.rename_doc("Account", name, new_name, ignore_permissions=1)
+ frappe.rename_doc("Account", name, new_name, force=1)
return new_name
@frappe.whitelist()
@@ -287,7 +287,7 @@ def merge_account(old, new, is_group, root_type, company):
frappe.db.set_value("Account", new, "parent_account",
frappe.db.get_value("Account", old, "parent_account"))
- frappe.rename_doc("Account", old, new, merge=1, ignore_permissions=1)
+ frappe.rename_doc("Account", old, new, merge=1, force=1)
return new
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 cae4c3c9b8..8856c8cc90 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
@@ -12,7 +12,7 @@
"Accrued Rebates Due from Suppliers": {
"account_type": "Receivable"
},
- "Accured Income from Suppliers": {
+ "Accrued Income from Suppliers": {
"account_type": "Receivable"
},
"Other Debtors": {
@@ -54,7 +54,7 @@
}
},
"Petty Cash": {
- "Petty Cash - Admininistration": {
+ "Petty Cash - Administration": {
"account_type": "Cash"
},
"Petty Cash - Others": {
@@ -85,13 +85,13 @@
"Handling Difference in Inventory": {
"account_type": "Stock Adjustment"
},
- "Items Delivered to Customs on temprary Base": {}
+ "Items Delivered to Customs on temporary Base": {}
},
"Stock in Hand": {
"account_type": "Stock"
}
},
- "Perliminary and Preoperating Expenses": {
+ "Preliminary and Preoperating Expenses": {
"Preoperating Expenses": {}
},
"Prepayments & Deposits": {
@@ -150,16 +150,16 @@
"account_type": "Fixed Asset"
},
"Leasehold Improvement": {},
- "Motor Vehicules": {
+ "Motor Vehicles": {
"account_type": "Fixed Asset"
},
- "Work In Progrees": {},
+ "Work In Progress": {},
"account_type": "Fixed Asset"
}
},
"Intangible Assets": {
"Computer Card Renewal": {},
- "Dispoal of Outlets": {},
+ "Disposal of Outlets": {},
"Registration of Trademarks": {}
},
"Intercompany Accounts": {},
@@ -218,7 +218,7 @@
},
"MISC Charges": {
"Other Charges": {
- "Captial Loss": {
+ "Capital Loss": {
"Disposal of Business Branch": {},
"Loss On Fixed Assets Disposal": {},
"Loss on Difference on Exchange": {}
@@ -253,14 +253,14 @@
"Other Bank Charges": {}
},
"Communications": {
- "Courrier": {},
+ "Courier": {},
"Others - Communication": {},
"Telephone": {},
"Web Site Hosting Fees": {}
},
"Office & Various Expenses": {
"Cleaning": {},
- "Convoyance Expenses": {},
+ "Conveyance Expenses": {},
"Gifts & Donations": {},
"Insurance": {},
"Kitchen and Buffet Expenses": {},
@@ -325,7 +325,7 @@
"Current Liabilities": {
"Accounts Payable": {
"Payables": {
- "Advance Paybale to Suppliers": {
+ "Advance Payable to Suppliers": {
"account_type": "Payable"
},
"Consigned Payable": {
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index be5407b926..64a5951f94 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -1,837 +1,203 @@
{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
- "creation": "2013-06-24 15:49:57",
- "custom": 0,
- "description": "Settings for Accounts",
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "Other",
- "editable_grid": 1,
- "engine": "InnoDB",
+ "creation": "2013-06-24 15:49:57",
+ "description": "Settings for Accounts",
+ "doctype": "DocType",
+ "document_type": "Other",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "auto_accounting_for_stock",
+ "acc_frozen_upto",
+ "frozen_accounts_modifier",
+ "determine_address_tax_category_from",
+ "column_break_4",
+ "credit_controller",
+ "check_supplier_invoice_uniqueness",
+ "make_payment_via_journal_entry",
+ "unlink_payment_on_cancellation_of_invoice",
+ "unlink_advance_payment_on_cancelation_of_order",
+ "book_asset_depreciation_entry_automatically",
+ "allow_cost_center_in_entry_of_bs_account",
+ "add_taxes_from_item_tax_template",
+ "automatically_fetch_payment_terms",
+ "print_settings",
+ "show_inclusive_tax_in_print",
+ "column_break_12",
+ "show_payment_schedule_in_print",
+ "currency_exchange_section",
+ "allow_stale",
+ "stale_days",
+ "report_settings_sb",
+ "use_custom_cash_flow"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "1",
- "description": "If enabled, the system will post accounting entries for inventory automatically.",
- "fetch_if_empty": 0,
- "fieldname": "auto_accounting_for_stock",
- "fieldtype": "Check",
- "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": "Make Accounting Entry For Every Stock Movement",
- "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
- },
+ "default": "1",
+ "description": "If enabled, the system will post accounting entries for inventory automatically.",
+ "fieldname": "auto_accounting_for_stock",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "in_list_view": 1,
+ "label": "Make Accounting Entry For Every Stock Movement"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "Accounting entry frozen up to this date, nobody can do / modify entry except role specified below.",
- "fetch_if_empty": 0,
- "fieldname": "acc_frozen_upto",
- "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": "Accounts Frozen Upto",
- "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
- },
+ "description": "Accounting entry frozen up to this date, nobody can do / modify entry except role specified below.",
+ "fieldname": "acc_frozen_upto",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "Accounts Frozen Upto"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "description": "Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts",
- "fetch_if_empty": 0,
- "fieldname": "frozen_accounts_modifier",
- "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": "Role Allowed to Set Frozen Accounts & Edit Frozen Entries",
- "length": 0,
- "no_copy": 0,
- "options": "Role",
- "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
- },
+ "description": "Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts",
+ "fieldname": "frozen_accounts_modifier",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Role Allowed to Set Frozen Accounts & Edit Frozen Entries",
+ "options": "Role"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "Billing Address",
- "description": "Address used to determine Tax Category in transactions.",
- "fetch_if_empty": 0,
- "fieldname": "determine_address_tax_category_from",
- "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": "Determine Address Tax Category From",
- "length": 0,
- "no_copy": 0,
- "options": "Billing Address\nShipping Address",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "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": "Billing Address",
+ "description": "Address used to determine Tax Category in transactions.",
+ "fieldname": "determine_address_tax_category_from",
+ "fieldtype": "Select",
+ "label": "Determine Address Tax Category From",
+ "options": "Billing Address\nShipping Address"
+ },
{
- "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,
- "description": "Role that is allowed to submit transactions that exceed credit limits set.",
- "fetch_if_empty": 0,
- "fieldname": "credit_controller",
- "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": "Credit Controller",
- "length": 0,
- "no_copy": 0,
- "options": "Role",
- "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
- },
+ "description": "Role that is allowed to submit transactions that exceed credit limits set.",
+ "fieldname": "credit_controller",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Credit Controller",
+ "options": "Role"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "check_supplier_invoice_uniqueness",
- "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": "Check Supplier Invoice Number Uniqueness",
- "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": "check_supplier_invoice_uniqueness",
+ "fieldtype": "Check",
+ "label": "Check Supplier Invoice Number Uniqueness"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "make_payment_via_journal_entry",
- "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": "Make Payment via Journal Entry",
- "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": "make_payment_via_journal_entry",
+ "fieldtype": "Check",
+ "label": "Make Payment via Journal Entry"
+ },
{
- "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": "unlink_payment_on_cancellation_of_invoice",
- "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": "Unlink Payment on Cancellation of Invoice",
- "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": "unlink_payment_on_cancellation_of_invoice",
+ "fieldtype": "Check",
+ "label": "Unlink Payment on Cancellation of Invoice"
+ },
{
- "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": "unlink_advance_payment_on_cancelation_of_order",
- "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": "Unlink Advance Payment on Cancelation of 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": "1",
+ "fieldname": "unlink_advance_payment_on_cancelation_of_order",
+ "fieldtype": "Check",
+ "label": "Unlink Advance Payment on Cancelation of Order"
+ },
{
- "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": "book_asset_depreciation_entry_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": "Book Asset Depreciation Entry 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
- },
+ "default": "1",
+ "fieldname": "book_asset_depreciation_entry_automatically",
+ "fieldtype": "Check",
+ "label": "Book Asset Depreciation Entry Automatically"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "allow_cost_center_in_entry_of_bs_account",
- "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 Cost Center In Entry of Balance Sheet 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": "allow_cost_center_in_entry_of_bs_account",
+ "fieldtype": "Check",
+ "label": "Allow Cost Center In Entry of Balance Sheet Account"
+ },
{
- "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": "add_taxes_from_item_tax_template",
- "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": "Automatically Add Taxes and Charges from Item Tax Template",
- "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": "add_taxes_from_item_tax_template",
+ "fieldtype": "Check",
+ "label": "Automatically Add Taxes and Charges from Item Tax Template"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "print_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": "Print 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
- },
+ "fieldname": "print_settings",
+ "fieldtype": "Section Break",
+ "label": "Print Settings"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "show_inclusive_tax_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": "Show Inclusive Tax 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
- },
+ "fieldname": "show_inclusive_tax_in_print",
+ "fieldtype": "Check",
+ "label": "Show Inclusive Tax In Print"
+ },
{
- "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_12",
- "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_12",
+ "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": "show_payment_schedule_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": "Show Payment Schedule 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
- },
+ "fieldname": "show_payment_schedule_in_print",
+ "fieldtype": "Check",
+ "label": "Show Payment Schedule in Print"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "currency_exchange_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": "Currency Exchange 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
- },
+ "fieldname": "currency_exchange_section",
+ "fieldtype": "Section Break",
+ "label": "Currency Exchange Settings"
+ },
{
- "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": "allow_stale",
- "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 Stale Exchange Rates",
- "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": "allow_stale",
+ "fieldtype": "Check",
+ "in_list_view": 1,
+ "label": "Allow Stale Exchange Rates"
+ },
{
- "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.allow_stale==0",
- "fetch_if_empty": 0,
- "fieldname": "stale_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": "Stale 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
- },
+ "default": "1",
+ "depends_on": "eval:doc.allow_stale==0",
+ "fieldname": "stale_days",
+ "fieldtype": "Int",
+ "label": "Stale Days"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_if_empty": 0,
- "fieldname": "report_settings_sb",
- "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": "Report 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
- },
+ "fieldname": "report_settings_sb",
+ "fieldtype": "Section Break",
+ "label": "Report Settings"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "0",
- "description": "Only select if you have setup Cash Flow Mapper documents",
- "fetch_if_empty": 0,
- "fieldname": "use_custom_cash_flow",
- "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 Custom Cash Flow Format",
- "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": "Only select if you have setup Cash Flow Mapper documents",
+ "fieldname": "use_custom_cash_flow",
+ "fieldtype": "Check",
+ "label": "Use Custom Cash Flow Format"
+ },
+ {
+ "fieldname": "automatically_fetch_payment_terms",
+ "fieldtype": "Check",
+ "label": "Automatically Fetch Payment Terms"
}
- ],
- "has_web_view": 0,
- "hide_toolbar": 0,
- "icon": "icon-cog",
- "idx": 1,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 1,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2019-04-06 12:28:43.026250",
- "modified_by": "Administrator",
- "module": "Accounts",
- "name": "Accounts Settings",
- "owner": "Administrator",
+ ],
+ "icon": "icon-cog",
+ "idx": 1,
+ "issingle": 1,
+ "modified": "2019-04-28 18:20:55.789946",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Accounts Settings",
+ "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": 0,
- "role": "Accounts Manager",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "role": "Accounts 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": "Sales User",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
- "write": 0
- },
+ "read": 1,
+ "role": "Sales User"
+ },
{
- "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": "Purchase User",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
- "write": 0
+ "read": 1,
+ "role": "Purchase User"
}
- ],
- "quick_entry": 1,
- "read_only": 0,
- "show_name_in_global_search": 0,
- "sort_order": "ASC",
- "track_changes": 1,
- "track_seen": 0,
- "track_views": 0
+ ],
+ "quick_entry": 1,
+ "sort_order": "ASC",
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 95d49a4421..97ad0ead3f 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -100,6 +100,7 @@ class PurchaseInvoice(BuyingController):
self.validate_fixed_asset()
self.create_remarks()
self.set_status()
+ self.validate_purchase_receipt_if_update_stock()
validate_inter_company_party(self.doctype, self.supplier, self.company, self.inter_company_invoice_reference)
def validate_release_date(self):
@@ -284,7 +285,7 @@ class PurchaseInvoice(BuyingController):
def update_status_updater_args(self):
if cint(self.update_stock):
- self.status_updater.extend([{
+ self.status_updater.append({
'source_dt': 'Purchase Invoice Item',
'target_dt': 'Purchase Order Item',
'join_field': 'po_detail',
@@ -292,28 +293,29 @@ class PurchaseInvoice(BuyingController):
'target_parent_dt': 'Purchase Order',
'target_parent_field': 'per_received',
'target_ref_field': 'qty',
- 'source_field': 'qty',
+ 'source_field': 'received_qty',
+ 'second_source_dt': 'Purchase Receipt Item',
+ 'second_source_field': 'received_qty',
+ 'second_join_field': 'purchase_order_item',
'percent_join_field':'purchase_order',
- # 'percent_join_field': 'prevdoc_docname',
'overflow_type': 'receipt',
'extra_cond': """ and exists(select name from `tabPurchase Invoice`
where name=`tabPurchase Invoice Item`.parent and update_stock = 1)"""
- },
- {
- 'source_dt': 'Purchase Invoice Item',
- 'target_dt': 'Purchase Order Item',
- 'join_field': 'po_detail',
- 'target_field': 'returned_qty',
- 'target_parent_dt': 'Purchase Order',
- # 'target_parent_field': 'per_received',
- # 'target_ref_field': 'qty',
- 'source_field': '-1 * qty',
- # 'percent_join_field': 'prevdoc_docname',
- # 'overflow_type': 'receipt',
- 'extra_cond': """ and exists (select name from `tabPurchase Invoice`
- where name=`tabPurchase Invoice Item`.parent and update_stock=1 and is_return=1)"""
- }
- ])
+ })
+ if cint(self.is_return):
+ self.status_updater.append({
+ 'source_dt': 'Purchase Invoice Item',
+ 'target_dt': 'Purchase Order Item',
+ 'join_field': 'po_detail',
+ 'target_field': 'returned_qty',
+ 'source_field': '-1 * qty',
+ 'second_source_dt': 'Purchase Receipt Item',
+ 'second_source_field': '-1 * qty',
+ 'second_join_field': 'purchase_order_item',
+ 'overflow_type': 'receipt',
+ 'extra_cond': """ and exists (select name from `tabPurchase Invoice`
+ where name=`tabPurchase Invoice Item`.parent and update_stock=1 and is_return=1)"""
+ })
def validate_purchase_receipt_if_update_stock(self):
if self.update_stock:
@@ -327,13 +329,13 @@ class PurchaseInvoice(BuyingController):
self.check_prev_docstatus()
self.update_status_updater_args()
+ self.update_prevdoc_status()
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
self.company, self.base_grand_total)
if not self.is_return:
self.update_against_document_in_jv()
- self.update_prevdoc_status()
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
self.update_billing_status_in_pr()
@@ -763,9 +765,9 @@ class PurchaseInvoice(BuyingController):
self.check_on_hold_or_closed_status()
self.update_status_updater_args()
+ self.update_prevdoc_status()
if not self.is_return:
- self.update_prevdoc_status()
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
self.update_billing_status_in_pr()
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index dde12d79c7..b4d584fbde 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -402,9 +402,9 @@ class TestPurchaseInvoice(unittest.TestCase):
pi.save()
pi.submit()
- self.assertEqual(pi.payment_schedule[0].payment_amount, 756.15)
+ self.assertEqual(pi.payment_schedule[0].payment_amount, 606.15)
self.assertEqual(pi.payment_schedule[0].due_date, pi.posting_date)
- self.assertEqual(pi.payment_schedule[1].payment_amount, 756.15)
+ self.assertEqual(pi.payment_schedule[1].payment_amount, 606.15)
self.assertEqual(pi.payment_schedule[1].due_date, add_days(pi.posting_date, 30))
pi.load_from_db()
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 31a9c66f6f..dd5f8fd4a1 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -254,7 +254,7 @@ class SalesInvoice(SellingController):
def update_status_updater_args(self):
if cint(self.update_stock):
- self.status_updater.extend([{
+ self.status_updater.append({
'source_dt':'Sales Invoice Item',
'target_dt':'Sales Order Item',
'target_parent_dt':'Sales Order',
@@ -272,21 +272,20 @@ class SalesInvoice(SellingController):
'overflow_type': 'delivery',
'extra_cond': """ and exists(select name from `tabSales Invoice`
where name=`tabSales Invoice Item`.parent and update_stock = 1)"""
- },
- {
- 'source_dt': 'Sales Invoice Item',
- 'target_dt': 'Sales Order Item',
- 'join_field': 'so_detail',
- 'target_field': 'returned_qty',
- 'target_parent_dt': 'Sales Order',
- # 'target_parent_field': 'per_delivered',
- # 'target_ref_field': 'qty',
- 'source_field': '-1 * qty',
- # 'percent_join_field': 'sales_order',
- # 'overflow_type': 'delivery',
- 'extra_cond': """ and exists (select name from `tabSales Invoice` where name=`tabSales Invoice Item`.parent and update_stock=1 and is_return=1)"""
- }
- ])
+ })
+ if cint(self.is_return):
+ self.status_updater.append({
+ 'source_dt': 'Sales Invoice Item',
+ 'target_dt': 'Sales Order Item',
+ 'join_field': 'so_detail',
+ 'target_field': 'returned_qty',
+ 'target_parent_dt': 'Sales Order',
+ 'source_field': '-1 * qty',
+ 'second_source_dt': 'Delivery Note Item',
+ 'second_source_field': '-1 * qty',
+ 'second_join_field': 'so_detail',
+ 'extra_cond': """ and exists (select name from `tabSales Invoice` where name=`tabSales Invoice Item`.parent and update_stock=1 and is_return=1)"""
+ })
def check_credit_limit(self):
from erpnext.selling.doctype.customer.customer import check_credit_limit
@@ -506,8 +505,8 @@ 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'):
- if frappe.get_cached_value('Item', d.item_code, 'is_stock_item') == 1 \
- and not d.get(i.lower().replace(' ','_')) and not self.get(dic[i][1]):
+ if (d.item_code and frappe.get_cached_value('Item', d.item_code, 'is_stock_item') == 1
+ 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)
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.js b/erpnext/accounts/report/gross_profit/gross_profit.js
index 1f7d24db1e..ba17a94e8d 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.js
+++ b/erpnext/accounts/report/gross_profit/gross_profit.js
@@ -23,6 +23,12 @@ frappe.query_reports["Gross Profit"] = {
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_end_date")
},
+ {
+ "fieldname":"sales_invoice",
+ "label": __("Sales Invoice"),
+ "fieldtype": "Link",
+ "options": "Sales Invoice"
+ },
{
"fieldname":"group_by",
"label": __("Group By"),
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py
index a5859e31b0..9a02d1b8e8 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/gross_profit.py
@@ -302,6 +302,12 @@ class GrossProfitGenerator(object):
sales_person_cols = ""
sales_team_table = ""
+ if self.filters.get("sales_invoice"):
+ conditions += " and `tabSales Invoice`.name = %(sales_invoice)s"
+
+ if self.filters.get("item_code"):
+ conditions += " and `tabSales Invoice Item`.item_code = %(item_code)s"
+
self.si_list = frappe.db.sql("""
select
`tabSales Invoice Item`.parenttype, `tabSales Invoice Item`.parent,
diff --git a/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.js b/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.js
index 36bd29efeb..7908c07a0a 100644
--- a/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.js
+++ b/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.js
@@ -4,6 +4,13 @@
frappe.query_reports["Inactive Sales Items"] = {
"filters": [
+ {
+ fieldname: "territory",
+ label: __("Territory"),
+ fieldtype: "Link",
+ options: "Territory",
+ reqd: 1,
+ },
{
fieldname: "item",
label: __("Item"),
diff --git a/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py b/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py
index 844942ff0f..b670e6e91f 100644
--- a/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py
+++ b/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.py
@@ -7,13 +7,11 @@ from frappe.utils import getdate, add_days, today, cint
from frappe import _
def execute(filters=None):
-
columns = get_columns()
data = get_data(filters)
return columns, data
def get_columns():
-
columns = [
{
"fieldname": "territory",
@@ -74,39 +72,39 @@ def get_columns():
def get_data(filters):
-
data = []
items = get_items(filters)
+ territories = get_territories(filters)
sales_invoice_data = get_sales_details(filters)
- for item in items:
- if sales_invoice_data.get(item.name):
- item_obj = sales_invoice_data[item.name]
- if item_obj.days_since_last_order > cint(filters['days']):
- row = {
- "territory": item_obj.territory,
- "item_group": item_obj.item_group,
- "item": item_obj.name,
- "item_name": item_obj.item_name,
- "customer": item_obj.customer,
- "last_order_date": item_obj.last_order_date,
- "qty": item_obj.qty,
- "days_since_last_order": item_obj.days_since_last_order
- }
- data.append(row)
- else:
+ for territory in territories:
+ for item in items:
row = {
+ "territory": territory.name,
"item_group": item.item_group,
"item": item.name,
"item_name": item.item_name
}
+
+ if sales_invoice_data.get((territory.name,item.name)):
+ item_obj = sales_invoice_data[(territory.name,item.name)]
+ if item_obj.days_since_last_order > cint(filters['days']):
+ row.update({
+ "territory": item_obj.territory,
+ "customer": item_obj.customer,
+ "last_order_date": item_obj.last_order_date,
+ "qty": item_obj.qty,
+ "days_since_last_order": item_obj.days_since_last_order
+ })
+ else:
+ continue
+
data.append(row)
return data
def get_sales_details(filters):
-
data = []
item_details_map = {}
@@ -121,12 +119,21 @@ def get_sales_details(filters):
.format(date_field = date_field, doctype = filters['based_on']), as_dict=1)
for d in sales_data:
- item_details_map.setdefault(d.item_name, d)
+ item_details_map.setdefault((d.territory,d.item_name), d)
return item_details_map
-def get_items(filters):
+def get_territories(filters):
+ filter_dict = {}
+ if filters.get("territory"):
+ filter_dict.update({'name': filters['territory']})
+
+ territories = frappe.get_all("Territory", fields=["name"], filters=filter_dict)
+
+ return territories
+
+def get_items(filters):
filters_dict = {
"disabled": 0,
"is_stock_item": 1
diff --git a/erpnext/accounts/report/utils.py b/erpnext/accounts/report/utils.py
index 8500aea415..4a9af490cf 100644
--- a/erpnext/accounts/report/utils.py
+++ b/erpnext/accounts/report/utils.py
@@ -135,3 +135,25 @@ def get_appropriate_company(filters):
company = get_default_company()
return company
+
+@frappe.whitelist()
+def get_invoiced_item_gross_margin(sales_invoice=None, item_code=None, company=None, with_item_data=False):
+ from erpnext.accounts.report.gross_profit.gross_profit import GrossProfitGenerator
+
+ sales_invoice = sales_invoice or frappe.form_dict.get('sales_invoice')
+ item_code = item_code or frappe.form_dict.get('item_code')
+ company = company or frappe.get_cached_value("Sales Invoice", sales_invoice, 'company')
+
+ filters = {
+ 'sales_invoice': sales_invoice,
+ 'item_code': item_code,
+ 'company': company,
+ 'group_by': 'Invoice'
+ }
+
+ gross_profit_data = GrossProfitGenerator(filters)
+ result = gross_profit_data.grouped_data
+ if not with_item_data:
+ result = sum([d.gross_profit for d in result])
+
+ return result
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index e5156a32b7..a9a2094f08 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -369,7 +369,9 @@ def make_purchase_receipt(source_name, target_doc=None):
"field_map": {
"name": "purchase_order_item",
"parent": "purchase_order",
- "bom": "bom"
+ "bom": "bom",
+ "material_request": "material_request",
+ "material_request_item": "material_request_item"
},
"postprocess": update_item,
"condition": lambda doc: abs(doc.received_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1
@@ -403,7 +405,7 @@ def make_purchase_invoice(source_name, target_doc=None):
or item.get("buying_cost_center")
or item_group.get("buying_cost_center"))
- doc = get_mapped_doc("Purchase Order", source_name, {
+ fields = {
"Purchase Order": {
"doctype": "Purchase Invoice",
"field_map": {
@@ -426,8 +428,16 @@ def make_purchase_invoice(source_name, target_doc=None):
"Purchase Taxes and Charges": {
"doctype": "Purchase Taxes and Charges",
"add_if_empty": True
+ },
+ }
+
+ if frappe.get_single("Accounts Settings").automatically_fetch_payment_terms == 1:
+ fields["Payment Schedule"] = {
+ "doctype": "Payment Schedule",
+ "add_if_empty": True
}
- }, target_doc, postprocess)
+
+ doc = get_mapped_doc("Purchase Order", source_name, fields, target_doc, postprocess)
return doc
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index 345490828a..820c93c8b7 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -6,7 +6,7 @@ import unittest
import frappe
import frappe.defaults
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
-from frappe.utils import flt, add_days, nowdate
+from frappe.utils import flt, add_days, nowdate, getdate
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.buying.doctype.purchase_order.purchase_order import (make_purchase_receipt, make_purchase_invoice, make_rm_stock_entry as make_subcontract_transfer_entry)
from erpnext.stock.doctype.material_request.test_material_request import make_material_request
@@ -108,6 +108,69 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertEqual(po.get("items")[0].amount, 1400)
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
+ def test_update_qty(self):
+ po = create_purchase_order()
+
+ make_pr_against_po(po.name, 6)
+
+ po.load_from_db()
+ self.assertEqual(po.get("items")[0].received_qty, 6)
+
+ # Check received_qty after make_purchase_invoice without update_stock checked
+ pi1 = make_purchase_invoice(po.name)
+ pi1.get("items")[0].qty = 6
+ pi1.insert()
+ pi1.submit()
+
+ po.load_from_db()
+ self.assertEqual(po.get("items")[0].received_qty, 6)
+
+ # Check received_qty after make_purchase_invoice with update_stock checked
+ pi2 = make_purchase_invoice(po.name)
+ pi2.set("update_stock", 1)
+ pi2.get("items")[0].qty = 3
+ pi2.insert()
+ pi2.submit()
+
+ po.load_from_db()
+ self.assertEqual(po.get("items")[0].received_qty, 9)
+
+ def test_return_against_purchase_order(self):
+ po = create_purchase_order()
+
+ pr = make_pr_against_po(po.name, 6)
+
+ po.load_from_db()
+ self.assertEqual(po.get("items")[0].received_qty, 6)
+
+ pi2 = make_purchase_invoice(po.name)
+ pi2.set("update_stock", 1)
+ pi2.get("items")[0].qty = 3
+ pi2.insert()
+ pi2.submit()
+
+ po.load_from_db()
+ self.assertEqual(po.get("items")[0].received_qty, 9)
+
+ # Make return purchase receipt, purchase invoice and check quantity
+ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \
+ import make_purchase_receipt as make_purchase_receipt_return
+ from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice \
+ import make_purchase_invoice as make_purchase_invoice_return
+
+ pr1 = make_purchase_receipt_return(is_return=1, return_against=pr.name, qty=-3, do_not_submit=True)
+ pr1.items[0].purchase_order = po.name
+ pr1.items[0].purchase_order_item = po.items[0].name
+ pr1.submit()
+
+ pi1= make_purchase_invoice_return(is_return=1, return_against=pi2.name, qty=-1, update_stock=1, do_not_submit=True)
+ pi1.items[0].purchase_order = po.name
+ pi1.items[0].po_detail = po.items[0].name
+ pi1.submit()
+
+
+ po.load_from_db()
+ self.assertEqual(po.get("items")[0].received_qty, 5)
def test_make_purchase_invoice(self):
po = create_purchase_order(do_not_submit=True)
@@ -142,9 +205,9 @@ class TestPurchaseOrder(unittest.TestCase):
po.submit()
self.assertEqual(po.payment_schedule[0].payment_amount, 2500.0)
- self.assertEqual(po.payment_schedule[0].due_date, po.transaction_date)
+ self.assertEqual(getdate(po.payment_schedule[0].due_date), getdate(po.transaction_date))
self.assertEqual(po.payment_schedule[1].payment_amount, 2500.0)
- self.assertEqual(po.payment_schedule[1].due_date, add_days(po.transaction_date, 30))
+ self.assertEqual(getdate(po.payment_schedule[1].due_date), add_days(getdate(po.transaction_date), 30))
pi = make_purchase_invoice(po.name)
pi.save()
@@ -152,9 +215,9 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertEqual(len(pi.get("items", [])), 1)
self.assertEqual(pi.payment_schedule[0].payment_amount, 2500.0)
- self.assertEqual(pi.payment_schedule[0].due_date, po.transaction_date)
+ self.assertEqual(getdate(pi.payment_schedule[0].due_date), getdate(po.transaction_date))
self.assertEqual(pi.payment_schedule[1].payment_amount, 2500.0)
- self.assertEqual(pi.payment_schedule[1].due_date, add_days(po.transaction_date, 30))
+ self.assertEqual(getdate(pi.payment_schedule[1].due_date), add_days(getdate(po.transaction_date), 30))
def test_subcontracting(self):
po = create_purchase_order(item_code="_Test FG Item", is_subcontracted="Yes")
@@ -303,6 +366,10 @@ class TestPurchaseOrder(unittest.TestCase):
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=20, basic_rate=100)
+ make_stock_entry(target="_Test Warehouse 1 - _TC", item_code="_Test Item",
+ qty=30, basic_rate=100)
+ make_stock_entry(target="_Test Warehouse 1 - _TC", item_code="_Test Item Home Desktop 100",
+ qty=30, basic_rate=100)
bin1 = frappe.db.get_value("Bin",
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
@@ -349,6 +416,11 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertEquals(bin5.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
+ make_stock_entry(target="_Test Warehouse 1 - _TC", item_code="_Test Item",
+ qty=40, basic_rate=100)
+ make_stock_entry(target="_Test Warehouse 1 - _TC", item_code="_Test Item Home Desktop 100",
+ qty=40, basic_rate=100)
+
# make Purchase Receipt against PO
pr = make_purchase_receipt(po.name)
pr.supplier_warehouse = "_Test Warehouse 1 - _TC"
@@ -501,6 +573,13 @@ class TestPurchaseOrder(unittest.TestCase):
frappe.db.set_value("Accounts Settings", "Accounts Settings",
"unlink_advance_payment_on_cancelation_of_order", 0)
+def make_pr_against_po(po, received_qty=0):
+ pr = make_purchase_receipt(po)
+ pr.get("items")[0].qty = received_qty or 5
+ pr.insert()
+ pr.submit()
+ return pr
+
def make_subcontracted_item(item_code):
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
diff --git a/erpnext/buying/report/procurement_tracker/__init__.py b/erpnext/buying/report/procurement_tracker/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.js b/erpnext/buying/report/procurement_tracker/procurement_tracker.js
new file mode 100644
index 0000000000..283d56c946
--- /dev/null
+++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.js
@@ -0,0 +1,39 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Procurement Tracker"] = {
+ "filters": [
+ {
+ fieldname: "company",
+ label: __("Company"),
+ fieldtype: "Link",
+ options: "Company",
+ default: frappe.defaults.get_user_default("Company"),
+ },
+ {
+ fieldname: "cost_center",
+ label: __("Cost Center"),
+ fieldtype: "Link",
+ options: "Cost Center",
+ },
+ {
+ fieldname: "project",
+ label: __("Project"),
+ fieldtype: "Link",
+ options: "Project",
+ },
+ {
+ fieldname: "from_date",
+ label: __("From Date"),
+ fieldtype: "Date",
+ default: frappe.defaults.get_user_default("year_start_date"),
+ },
+ {
+ fieldname:"to_date",
+ label: __("To Date"),
+ fieldtype: "Date",
+ default: frappe.defaults.get_user_default("year_end_date"),
+ },
+ ]
+}
diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.json b/erpnext/buying/report/procurement_tracker/procurement_tracker.json
new file mode 100644
index 0000000000..028736c7c4
--- /dev/null
+++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.json
@@ -0,0 +1,20 @@
+{
+ "add_total_row": 1,
+ "creation": "2019-03-29 17:05:45.196949",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "modified": "2019-03-29 17:18:06.678728",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Procurement Tracker",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Purchase Order",
+ "report_name": "Procurement Tracker",
+ "report_type": "Script Report",
+ "roles": []
+}
\ 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
new file mode 100644
index 0000000000..d3ee447545
--- /dev/null
+++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.py
@@ -0,0 +1,282 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+
+def execute(filters=None):
+ columns = get_columns(filters)
+ data = get_data(filters)
+ return columns, data
+
+def get_columns(filters):
+ columns = [
+ {
+ "label": _("Material Request Date"),
+ "fieldname": "material_request_date",
+ "fieldtype": "Date",
+ "width": 140
+ },
+ {
+ "label": _("Material Request No"),
+ "options": "Material Request",
+ "fieldname": "material_request_no",
+ "fieldtype": "Link",
+ "width": 140
+ },
+ {
+ "label": _("Cost Center"),
+ "options": "Cost Center",
+ "fieldname": "cost_center",
+ "fieldtype": "Link",
+ "width": 140
+ },
+ {
+ "label": _("Project"),
+ "options": "Project",
+ "fieldname": "project",
+ "fieldtype": "Link",
+ "width": 140
+ },
+ {
+ "label": _("Requesting Site"),
+ "options": "Warehouse",
+ "fieldname": "requesting_site",
+ "fieldtype": "Link",
+ "width": 140
+ },
+ {
+ "label": _("Requestor"),
+ "options": "Employee",
+ "fieldname": "requestor",
+ "fieldtype": "Link",
+ "width": 140
+ },
+ {
+ "label": _("Description"),
+ "fieldname": "description",
+ "fieldtype": "Data",
+ "width": 200
+ },
+ {
+ "label": _("Quantity"),
+ "fieldname": "quantity",
+ "fieldtype": "Int",
+ "width": 140
+ },
+ {
+ "label": _("Unit of Measure"),
+ "options": "UOM",
+ "fieldname": "unit_of_measurement",
+ "fieldtype": "Link",
+ "width": 140
+ },
+ {
+ "label": _("Status"),
+ "fieldname": "status",
+ "fieldtype": "data",
+ "width": 140
+ },
+ {
+ "label": _("Purchase Order Date"),
+ "fieldname": "purchase_order_date",
+ "fieldtype": "Date",
+ "width": 140
+ },
+ {
+ "label": _("Purchase Order"),
+ "options": "Purchase Order",
+ "fieldname": "purchase_order",
+ "fieldtype": "Link",
+ "width": 140
+ },
+ {
+ "label": _("Supplier"),
+ "options": "Supplier",
+ "fieldname": "supplier",
+ "fieldtype": "Link",
+ "width": 140
+ },
+ {
+ "label": _("Estimated Cost"),
+ "fieldname": "estimated_cost",
+ "fieldtype": "Float",
+ "width": 140
+ },
+ {
+ "label": _("Actual Cost"),
+ "fieldname": "actual_cost",
+ "fieldtype": "Float",
+ "width": 140
+ },
+ {
+ "label": _("Purchase Order Amount"),
+ "fieldname": "purchase_order_amt",
+ "fieldtype": "Float",
+ "width": 140
+ },
+ {
+ "label": _("Purchase Order Amount(Company Currency)"),
+ "fieldname": "purchase_order_amt_usd",
+ "fieldtype": "Float",
+ "width": 140
+ },
+ {
+ "label": _("Expected Delivery Date"),
+ "fieldname": "expected_delivery_date",
+ "fieldtype": "Date",
+ "width": 140
+ },
+ {
+ "label": _("Actual Delivery Date"),
+ "fieldname": "actual_delivery_date",
+ "fieldtype": "Date",
+ "width": 140
+ },
+ ]
+ return columns
+
+def get_conditions(filters):
+ conditions = ""
+
+ if filters.get("company"):
+ conditions += " AND company='%s'"% 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'))
+
+ if filters.get("from_date"):
+ conditions += "AND transaction_date>=%s"% filters.get('from_date')
+
+ if filters.get("to_date"):
+ conditions += "AND transaction_date<=%s"% filters.get('to_date')
+ return conditions
+
+def get_data(filters):
+ conditions = get_conditions(filters)
+ purchase_order_entry = get_po_entries(conditions)
+ mr_records, procurement_record_against_mr = get_mapped_mr_details(conditions)
+ pr_records = get_mapped_pr_records()
+ pi_records = get_mapped_pi_records()
+ print(pi_records)
+
+ procurement_record=[]
+ if procurement_record_against_mr:
+ procurement_record += procurement_record_against_mr
+ for po in purchase_order_entry:
+ # fetch material records linked to the purchase order item
+ mr_record = mr_records.get(po.material_request_item, [{}])[0]
+ procurement_detail = {
+ "material_request_date": mr_record.get('transaction_date'),
+ "cost_center": po.cost_center,
+ "project": po.project,
+ "requesting_site": po.warehouse,
+ "requestor": po.owner,
+ "material_request_no": po.material_request,
+ "description": po.description,
+ "quantity": po.qty,
+ "unit_of_measurement": po.stock_uom,
+ "status": po.status,
+ "purchase_order_date": po.transaction_date,
+ "purchase_order": po.parent,
+ "supplier": po.supplier,
+ "estimated_cost": mr_record.get('amount'),
+ "actual_cost": pi_records.get(po.name),
+ "purchase_order_amt": po.amount,
+ "purchase_order_amt_in_company_currency": po.base_amount,
+ "expected_delivery_date": po.schedule_date,
+ "actual_delivery_date": pr_records.get(po.name)
+ }
+ procurement_record.append(procurement_detail)
+ return procurement_record
+
+def get_mapped_mr_details(conditions):
+ mr_records = {}
+ mr_details = frappe.db.sql("""
+ SELECT
+ mr.transaction_date,
+ mr.per_ordered,
+ mr_item.name,
+ mr_item.parent,
+ mr_item.amount
+ FROM `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
+ WHERE
+ mr.per_ordered>=0
+ AND mr.name=mr_item.parent
+ AND mr.docstatus=1
+ {conditions}
+ """.format(conditions=conditions), as_dict=1) #nosec
+
+ procurement_record_against_mr = []
+ for record in mr_details:
+ if record.per_ordered:
+ mr_records.setdefault(record.name, []).append(frappe._dict(record))
+ else:
+ procurement_record_details = dict(
+ material_request_date=record.transaction_date,
+ material_request_no=record.parent,
+ estimated_cost=record.amount
+ )
+ procurement_record_against_mr.append(procurement_record_details)
+ return mr_records, procurement_record_against_mr
+
+def get_mapped_pi_records():
+ return frappe._dict(frappe.db.sql("""
+ SELECT
+ pi_item.po_detail,
+ pi_item.base_amount
+ FROM `tabPurchase Invoice Item` as pi_item
+ INNER JOIN `tabPurchase Order` as po
+ ON pi_item.`purchase_order` = po.`name`
+ WHERE
+ pi_item.docstatus = 1
+ AND po.status not in ("Closed","Completed","Cancelled")
+ AND pi_item.po_detail IS NOT NULL
+ """))
+
+def get_mapped_pr_records():
+ return frappe._dict(frappe.db.sql("""
+ SELECT
+ pr_item.purchase_order_item,
+ pr.posting_date
+ FROM `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pr_item
+ WHERE
+ pr.docstatus=1
+ AND pr.name=pr_item.parent
+ AND pr_item.purchase_order_item IS NOT NULL
+ AND pr.status not in ("Closed","Completed","Cancelled")
+ """))
+
+def get_po_entries(conditions):
+ return frappe.db.sql("""
+ SELECT
+ po_item.name,
+ po_item.parent,
+ po_item.cost_center,
+ po_item.project,
+ po_item.warehouse,
+ po_item.material_request,
+ po_item.material_request_item,
+ po_item.description,
+ po_item.stock_uom,
+ po_item.qty,
+ po_item.amount,
+ po_item.base_amount,
+ po_item.schedule_date,
+ po.transaction_date,
+ po.supplier,
+ po.status,
+ po.owner
+ FROM `tabPurchase Order` po, `tabPurchase Order Item` po_item
+ WHERE
+ po.docstatus = 1
+ AND po.name = po_item.parent
+ AND po.status not in ("Closed","Completed","Cancelled")
+ {conditions}
+ GROUP BY
+ po.name,po_item.item_code
+ """.format(conditions=conditions), as_dict=1) #nosec
\ No newline at end of file
diff --git a/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py
new file mode 100644
index 0000000000..4a13f385bb
--- /dev/null
+++ b/erpnext/buying/report/procurement_tracker/test_procurement_tracker.py
@@ -0,0 +1,67 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import unittest
+from datetime import datetime
+import frappe
+from erpnext.buying.report.procurement_tracker.procurement_tracker import execute
+from erpnext.stock.doctype.material_request.test_material_request import make_material_request
+from erpnext.stock.doctype.material_request.material_request import make_purchase_order
+from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
+from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+
+class TestProcurementTracker(unittest.TestCase):
+ def test_result_for_procurement_tracker(self):
+ filters = {
+ 'company': '_Test Procurement Company',
+ 'cost_center': '_Test Cost Center - _TC'
+ }
+ expected_data = self.generate_expected_data()
+ report = execute(filters)
+
+ length = len(report[1])
+ self.assertEqual(expected_data, report[1][length-1])
+
+ def generate_expected_data(self):
+ if not frappe.db.exists("Company", "_Test Procurement Company"):
+ frappe.get_doc(dict(
+ doctype="Company",
+ company_name="_Test Procurement Company",
+ abbr="_TPC",
+ default_currency="INR",
+ country="India"
+ )).insert()
+ warehouse = create_warehouse("_Test Procurement Warehouse", company="_Test Procurement Company")
+ mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse)
+ po = make_purchase_order(mr.name)
+ po.supplier = "_Test Supplier"
+ po.get("items")[0].cost_center = "_Test Cost Center - _TC"
+ po.submit()
+ pr = make_purchase_receipt(po.name)
+ pr.submit()
+ frappe.db.commit()
+ date_obj = datetime.date(datetime.now())
+
+ expected_data = {
+ "material_request_date": date_obj,
+ "cost_center": "_Test Cost Center - _TC",
+ "project": None,
+ "requesting_site": "_Test Procurement Warehouse - _TPC",
+ "requestor": "Administrator",
+ "material_request_no": mr.name,
+ "description": '_Test Item 1',
+ "quantity": 10.0,
+ "unit_of_measurement": "_Test UOM",
+ "status": "To Bill",
+ "purchase_order_date": date_obj,
+ "purchase_order": po.name,
+ "supplier": "_Test Supplier",
+ "estimated_cost": 0.0,
+ "actual_cost": None,
+ "purchase_order_amt": 5000.0,
+ "purchase_order_amt_in_company_currency": 300000.0,
+ "expected_delivery_date": date_obj,
+ "actual_delivery_date": date_obj
+ }
+ return expected_data
\ No newline at end of file
diff --git a/erpnext/config/selling.py b/erpnext/config/selling.py
index 400f6be826..63e7ad965d 100644
--- a/erpnext/config/selling.py
+++ b/erpnext/config/selling.py
@@ -10,7 +10,7 @@ def get_data():
{
"type": "doctype",
"name": "Customer",
- "description": _("Customer database."),
+ "description": _("Customer Database."),
"onboard": 1,
},
{
@@ -34,6 +34,13 @@ def get_data():
"onboard": 1,
"dependencies": ["Item", "Customer"],
},
+ {
+ "type": "doctype",
+ "name": "Blanket Order",
+ "description": _("Blanket Orders from Costumers."),
+ "onboard": 1,
+ "dependencies": ["Item", "Customer"],
+ },
{
"type": "doctype",
"name": "Sales Partner",
diff --git a/erpnext/config/settings.py b/erpnext/config/settings.py
index a97e8ce5d7..323683a3e6 100644
--- a/erpnext/config/settings.py
+++ b/erpnext/config/settings.py
@@ -90,12 +90,6 @@ def get_data():
"label": _("Email"),
"icon": "fa fa-envelope",
"items": [
- {
- "type": "doctype",
- "name": "Feedback Trigger",
- "label": _("Feedback Trigger"),
- "description": _("Automatically triggers the feedback request based on conditions.")
- },
{
"type": "doctype",
"name": "Email Digest",
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 155a996a15..0672b2d0cc 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -787,6 +787,9 @@ class AccountsController(TransactionBase):
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
grand_total = grand_total - flt(self.write_off_amount)
+ if self.get("total_advance"):
+ grand_total -= self.get("total_advance")
+
if not self.get("payment_schedule"):
if self.get("payment_terms_template"):
data = get_payment_terms(self.payment_terms_template, posting_date, grand_total)
@@ -832,6 +835,9 @@ class AccountsController(TransactionBase):
total = flt(total, self.precision("grand_total"))
grand_total = flt(self.get("rounded_total") or self.grand_total, self.precision('grand_total'))
+ if self.get("total_advance"):
+ grand_total -= self.get("total_advance")
+
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
grand_total = grand_total - flt(self.write_off_amount)
if total != grand_total:
diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py
index e0acd73edf..631f7751b4 100644
--- a/erpnext/controllers/item_variant.py
+++ b/erpnext/controllers/item_variant.py
@@ -124,16 +124,8 @@ def find_variant(template, args, variant_item_code=None):
conditions = " or ".join(conditions)
- # use approximate match and shortlist possible variant matches
- # it is approximate because we are matching using OR condition
- # and it need not be exact match at this stage
- # this uses a simpler query instead of using multiple exists conditions
- possible_variants = frappe.db.sql_list("""select name from `tabItem` item
- where variant_of=%s and exists (
- select name from `tabItem Variant Attribute` iv_attribute
- where iv_attribute.parent=item.name
- and ({conditions}) and parent != %s
- )""".format(conditions=conditions), (template, cstr(variant_item_code)))
+ from erpnext.portal.product_configurator.utils import get_item_codes_by_attributes
+ possible_variants = [i for i in get_item_codes_by_attributes(args, template) if i != variant_item_code]
for variant in possible_variants:
variant = frappe.get_doc("Item", variant)
@@ -317,7 +309,7 @@ def make_variant_item_code(template_item_code, template_item_name, variant):
}, as_dict=True)
if not item_attribute:
- return
+ continue
# frappe.throw(_('Invalid attribute {0} {1}').format(frappe.bold(attr.attribute),
# frappe.bold(attr.attribute_value)), title=_('Invalid Attribute'),
# exc=InvalidItemAttributeValueError)
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index 1bf03ec803..22846693d1 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -253,11 +253,13 @@ def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len,
and return_against in (select name from `tabDelivery Note` where per_billed < 100)
)
)
- %(mcond)s order by `tabDelivery Note`.`%(key)s` asc
+ %(mcond)s order by `tabDelivery Note`.`%(key)s` asc limit %(start)s, %(page_len)s
""" % {
"key": searchfield,
"fcond": get_filters_cond(doctype, filters, []),
"mcond": get_match_cond(doctype),
+ "start": start,
+ "page_len": page_len,
"txt": "%(txt)s"
}, {"txt": ("%%%s%%" % txt)}, as_dict=as_dict)
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index 77d941c98e..3107c9274f 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -96,7 +96,9 @@ status_map = {
["Partially Ordered", "eval:self.status != 'Stopped' and self.per_ordered < 100 and self.per_ordered > 0 and self.docstatus == 1"],
["Ordered", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'"],
["Transferred", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Transfer'"],
- ["Issued", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Issue'"]
+ ["Issued", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Issue'"],
+ ["Received", "eval:self.status != 'Stopped' and self.per_received == 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'"],
+ ["Partially Received", "eval:self.status != 'Stopped' and self.per_received > 0 and self.per_received < 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'"]
]
}
diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js
index 208b1f55fa..8c1ab2f38f 100644
--- a/erpnext/crm/doctype/lead/lead.js
+++ b/erpnext/crm/doctype/lead/lead.js
@@ -5,29 +5,32 @@ frappe.provide("erpnext");
cur_frm.email_field = "email_id";
erpnext.LeadController = frappe.ui.form.Controller.extend({
- setup: function() {
- this.frm.fields_dict.customer.get_query = function(doc, cdt, cdn) {
- return { query: "erpnext.controllers.queries.customer_query" } }
+ setup: function () {
+ this.frm.fields_dict.customer.get_query = function (doc, cdt, cdn) {
+ return { query: "erpnext.controllers.queries.customer_query" }
+ }
+
+ 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) {
+ 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" }
}
}
- 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" } }
+ 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" }
+ }
}
},
- refresh: function() {
+ refresh: function () {
var doc = this.frm.doc;
erpnext.toggle_naming_series();
- frappe.dynamic_link = {doc: doc, fieldname: 'name', doctype: 'Lead'}
+ 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'));
@@ -35,49 +38,46 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
this.frm.add_custom_button(__("Quotation"), this.make_quotation, __('Create'));
}
- if(!this.frm.doc.__islocal) {
+ if (!this.frm.doc.__islocal) {
frappe.contacts.render_address_and_contact(cur_frm);
} else {
frappe.contacts.clear_address_and_contact(cur_frm);
}
},
- create_customer: function() {
+ create_customer: function () {
frappe.model.open_mapped_doc({
method: "erpnext.crm.doctype.lead.lead.make_customer",
frm: cur_frm
})
},
- create_opportunity: function() {
+ create_opportunity: function () {
frappe.model.open_mapped_doc({
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
frm: cur_frm
})
},
- make_quotation: function() {
+ make_quotation: function () {
frappe.model.open_mapped_doc({
method: "erpnext.crm.doctype.lead.lead.make_quotation",
frm: cur_frm
})
},
- organization_lead: function() {
- if (this.frm.doc.organization_lead == 1) {
- this.frm.set_df_property('company_name', 'reqd', 1);
- } else {
- this.frm.set_df_property('company_name', 'reqd', 0);
- }
+ organization_lead: function () {
+ this.frm.toggle_reqd("lead_name", !this.frm.doc.organization_lead);
+ this.frm.toggle_reqd("company_name", this.frm.doc.organization_lead);
},
- company_name: function() {
+ company_name: function () {
if (this.frm.doc.organization_lead == 1) {
this.frm.set_value("lead_name", this.frm.doc.company_name);
}
},
- contact_date: function() {
+ contact_date: function () {
if (this.frm.doc.contact_date) {
let d = moment(this.frm.doc.contact_date);
d.add(1, "hours");
@@ -86,4 +86,4 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
}
});
-$.extend(cur_frm.cscript, new erpnext.LeadController({frm: cur_frm}));
+$.extend(cur_frm.cscript, new erpnext.LeadController({ frm: cur_frm }));
diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json
index f1c2a9b4f1..600e070b7c 100644
--- a/erpnext/crm/doctype/lead/lead.json
+++ b/erpnext/crm/doctype/lead/lead.json
@@ -145,7 +145,7 @@
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
- "reqd": 1,
+ "reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
@@ -268,7 +268,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
- "in_list_view": 0,
+ "in_list_view": 1,
"in_standard_filter": 0,
"label": "Lead Owner",
"length": 0,
@@ -1419,17 +1419,18 @@
}
],
"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,
- "menu_index": 0,
- "modified": "2019-04-11 22:12:50.029368",
+ "modified": "2019-05-10 03:22:57.283628",
"modified_by": "Administrator",
"module": "CRM",
"name": "Lead",
diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py
index d42502dc3b..6e98ebb6c0 100644
--- a/erpnext/crm/doctype/lead/lead.py
+++ b/erpnext/crm/doctype/lead/lead.py
@@ -109,7 +109,11 @@ class Lead(SellingController):
def set_lead_name(self):
if not self.lead_name:
- frappe.db.set_value("Lead", self.name, "lead_name", self.company_name)
+ # Check for leads being created through data import
+ if not self.company_name:
+ frappe.throw(_("A Lead requires either a person's name or an organization's name"))
+
+ self.lead_name = self.company_name
@frappe.whitelist()
def make_customer(source_name, target_doc=None):
@@ -225,4 +229,4 @@ def make_lead_from_communication(communication, ignore_communication_links=False
lead_name = lead.name
link_communication_to_document(doc, "Lead", lead_name, ignore_communication_links)
- return lead_name
\ No newline at end of file
+ return lead_name
diff --git a/erpnext/education/doctype/course/course.py b/erpnext/education/doctype/course/course.py
index 987823a01e..0747a22f8d 100644
--- a/erpnext/education/doctype/course/course.py
+++ b/erpnext/education/doctype/course/course.py
@@ -20,9 +20,9 @@ class Course(Document):
frappe.throw(_("Total Weightage of all Assessment Criteria must be 100%"))
def get_topics(self):
- try:
- topic_list = self.get_all_children()
- topic_data = [frappe.get_doc("Topic", topic.topic) for topic in topic_list]
- except frappe.DoesNotExistError:
- return None
+ topic_data= []
+ for topic in self.topics:
+ topic_doc = frappe.get_doc("Topic", topic.topic)
+ if topic_doc.topic_content:
+ topic_data.append(topic_doc)
return topic_data
\ No newline at end of file
diff --git a/erpnext/education/doctype/course/test_course.py b/erpnext/education/doctype/course/test_course.py
index a24ba8a527..4667ac45a2 100644
--- a/erpnext/education/doctype/course/test_course.py
+++ b/erpnext/education/doctype/course/test_course.py
@@ -3,6 +3,7 @@
# See license.txt
from __future__ import unicode_literals
from erpnext.education.doctype.topic.test_topic import make_topic
+from erpnext.education.doctype.topic.test_topic import make_topic_and_linked_content
import frappe
import unittest
@@ -11,6 +12,8 @@ import unittest
class TestCourse(unittest.TestCase):
def setUp(self):
+ make_topic_and_linked_content("_Test Topic 1", [{"type":"Article", "name": "_Test Article 1"}])
+ make_topic_and_linked_content("_Test Topic 2", [{"type":"Article", "name": "_Test Article 2"}])
make_course_and_linked_topic("_Test Course 1", ["_Test Topic 1", "_Test Topic 2"])
def test_get_topics(self):
diff --git a/erpnext/education/doctype/course_enrollment/course_enrollment.py b/erpnext/education/doctype/course_enrollment/course_enrollment.py
index 9508636d2f..6f2bb0db1f 100644
--- a/erpnext/education/doctype/course_enrollment/course_enrollment.py
+++ b/erpnext/education/doctype/course_enrollment/course_enrollment.py
@@ -21,7 +21,10 @@ class CourseEnrollment(Document):
progress = []
for topic in topics:
progress.append(student.get_topic_progress(self.name, topic))
- return reduce(lambda x,y: x+y, progress) # Flatten out the List
+ if progress:
+ return reduce(lambda x,y: x+y, progress) # Flatten out the List
+ else:
+ return []
def validate_duplication(self):
enrollment = frappe.get_all("Course Enrollment", filters={
diff --git a/erpnext/education/doctype/education_settings/education_settings.json b/erpnext/education/doctype/education_settings/education_settings.json
index 3be4988d21..32b5fb8198 100644
--- a/erpnext/education/doctype/education_settings/education_settings.json
+++ b/erpnext/education/doctype/education_settings/education_settings.json
@@ -1,492 +1,136 @@
{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
"creation": "2017-04-05 13:33:04.519313",
- "custom": 0,
- "docstatus": 0,
"doctype": "DocType",
- "document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
+ "field_order": [
+ "current_academic_year",
+ "current_academic_term",
+ "attendance_freeze_date",
+ "column_break_4",
+ "validate_batch",
+ "validate_course",
+ "academic_term_reqd",
+ "section_break_7",
+ "instructor_created_by",
+ "web_academy_settings_section",
+ "enable_lms",
+ "portal_title",
+ "description"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "current_academic_year",
"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": "Current Academic Year",
- "length": 0,
- "no_copy": 0,
- "options": "Academic Year",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "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": "Academic Year"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "current_academic_term",
"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": "Current Academic Term",
- "length": 0,
- "no_copy": 0,
- "options": "Academic 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
+ "options": "Academic Term"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "attendance_freeze_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": "Attendance Freeze 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
+ "label": "Attendance Freeze Date"
},
{
- "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
+ "fieldtype": "Column Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"description": "For Batch based Student Group, the Student Batch will be validated for every Student from the Program Enrollment.",
"fieldname": "validate_batch",
"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": "Validate Batch for Students in Student 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": "Validate Batch for Students in Student Group"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"description": "For Course based Student Group, the Course will be validated for every Student from the enrolled Courses in Program Enrollment.",
"fieldname": "validate_course",
"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": "Validate Enrolled Course for Students in Student 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": "Validate Enrolled Course for Students in Student Group"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"default": "0",
"description": "If enabled, field Academic Term will be Mandatory in Program Enrollment Tool.",
"fieldname": "academic_term_reqd",
"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": "Make Academic Term Mandatory",
- "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": "Make Academic Term Mandatory"
},
{
- "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
+ "fieldtype": "Section Break"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"default": "Full Name",
"fieldname": "instructor_created_by",
"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": "Instructor Records to be created by",
- "length": 0,
- "no_copy": 0,
- "options": "Full Name\nNaming Series\nEmployee Number",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "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": "Full Name\nNaming Series\nEmployee Number"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "web_academy_settings_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": "LMS 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": "LMS Settings"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
+ "depends_on": "eval: doc.enable_lms",
"fieldname": "portal_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": "Portal 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
+ "label": "LMS Title"
},
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
+ "depends_on": "eval: doc.enable_lms",
"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,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "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"
+ },
+ {
+ "fieldname": "enable_lms",
+ "fieldtype": "Check",
+ "label": "Enable LMS"
}
],
- "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-12-11 15:49:15.045116",
+ "modified": "2019-05-13 18:36:13.127563",
"modified_by": "Administrator",
"module": "Education",
"name": "Education 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": "Education Manager",
- "set_user_permissions": 0,
"share": 1,
- "submit": 0,
"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": 0,
"role": "Guest",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
- "write": 0
+ "share": 1
}
],
"quick_entry": 1,
- "read_only": 0,
- "read_only_onload": 0,
"restrict_to_domain": "Education",
- "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/education/doctype/education_settings/education_settings.py b/erpnext/education/doctype/education_settings/education_settings.py
index 9286efa7c4..a85d3e70f3 100644
--- a/erpnext/education/doctype/education_settings/education_settings.py
+++ b/erpnext/education/doctype/education_settings/education_settings.py
@@ -34,3 +34,6 @@ class EducationSettings(Document):
make_property_setter('Instructor', "naming_series", "hidden", 0, "Check")
else:
make_property_setter('Instructor', "naming_series", "hidden", 1, "Check")
+
+def update_website_context(context):
+ context["lms_enabled"] = frappe.get_doc("Education Settings").enable_lms
\ No newline at end of file
diff --git a/erpnext/education/doctype/program/program.py b/erpnext/education/doctype/program/program.py
index dbeda40775..d24df5d614 100644
--- a/erpnext/education/doctype/program/program.py
+++ b/erpnext/education/doctype/program/program.py
@@ -9,6 +9,6 @@ from frappe.model.document import Document
class Program(Document):
def get_course_list(self):
- program_course_list = self.get_all_children()
+ program_course_list = self.courses
course_list = [frappe.get_doc("Course", program_course.course) for program_course in program_course_list]
return course_list
\ No newline at end of file
diff --git a/erpnext/education/doctype/student/student.py b/erpnext/education/doctype/student/student.py
index cf8407c4ea..529f78d4f5 100644
--- a/erpnext/education/doctype/student/student.py
+++ b/erpnext/education/doctype/student/student.py
@@ -90,13 +90,14 @@ class Student(Document):
"""
contents = topic.get_contents()
progress = []
- for content in contents:
- if content.doctype in ('Article', 'Video'):
- status = check_content_completion(content.name, content.doctype, course_enrollment_name)
- progress.append({'content': content.name, 'content_type': content.doctype, 'is_complete': status})
- elif content.doctype == 'Quiz':
- status, score, result = check_quiz_completion(content, course_enrollment_name)
- progress.append({'content': content.name, 'content_type': content.doctype, 'is_complete': status, 'score': score, 'result': result})
+ if contents:
+ for content in contents:
+ if content.doctype in ('Article', 'Video'):
+ status = check_content_completion(content.name, content.doctype, course_enrollment_name)
+ progress.append({'content': content.name, 'content_type': content.doctype, 'is_complete': status})
+ elif content.doctype == 'Quiz':
+ status, score, result = check_quiz_completion(content, course_enrollment_name)
+ progress.append({'content': content.name, 'content_type': content.doctype, 'is_complete': status, 'score': score, 'result': result})
return progress
def enroll_in_program(self, program_name):
diff --git a/erpnext/education/doctype/topic/topic.py b/erpnext/education/doctype/topic/topic.py
index 339fc7d887..7e5da329eb 100644
--- a/erpnext/education/doctype/topic/topic.py
+++ b/erpnext/education/doctype/topic/topic.py
@@ -9,7 +9,7 @@ from frappe.model.document import Document
class Topic(Document):
def get_contents(self):
try:
- topic_content_list = self.get_all_children()
+ topic_content_list = self.topic_content
content_data = [frappe.get_doc(topic_content.content_type, topic_content.content) for topic_content in topic_content_list]
except Exception as e:
frappe.log_error(frappe.get_traceback())
diff --git a/erpnext/education/doctype/topic_content/topic_content.json b/erpnext/education/doctype/topic_content/topic_content.json
index 52207882e0..444fd1d07c 100644
--- a/erpnext/education/doctype/topic_content/topic_content.json
+++ b/erpnext/education/doctype/topic_content/topic_content.json
@@ -1,140 +1,44 @@
{
- "allow_copy": 0,
- "allow_events_in_timeline": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "beta": 0,
"creation": "2018-12-12 11:42:57.987434",
- "custom": 0,
- "docstatus": 0,
"doctype": "DocType",
- "document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
+ "field_order": [
+ "content_type",
+ "column_break_2",
+ "content"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
"fieldname": "content_type",
"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": "Content Type",
- "length": 0,
- "no_copy": 0,
"options": "\nArticle\nVideo\nQuiz",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "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": "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": "content",
"fieldtype": "Dynamic 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": "Content",
- "length": 0,
- "no_copy": 0,
"options": "content_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": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "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": "2018-12-12 11:46:46.112018",
+ "modified": "2019-05-14 11:12:49.153771",
"modified_by": "Administrator",
"module": "Education",
"name": "Topic Content",
- "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,
- "track_views": 0
+ "track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 8ae9267b64..53da013f8d 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -50,7 +50,7 @@ on_logout = "erpnext.shopping_cart.utils.clear_cart_count"
treeviews = ['Account', 'Cost Center', 'Warehouse', 'Item Group', 'Customer Group', 'Sales Person', 'Territory', 'Assessment Group']
# website
-update_website_context = "erpnext.shopping_cart.utils.update_website_context"
+update_website_context = ["erpnext.shopping_cart.utils.update_website_context", "erpnext.education.doctype.education_settings.education_settings.update_website_context"]
my_account_context = "erpnext.shopping_cart.utils.update_my_account_context"
email_append_to = ["Job Applicant", "Lead", "Opportunity", "Issue"]
diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.js b/erpnext/hr/doctype/hr_settings/hr_settings.js
index 6ab523eb76..d8be46bee7 100644
--- a/erpnext/hr/doctype/hr_settings/hr_settings.js
+++ b/erpnext/hr/doctype/hr_settings/hr_settings.js
@@ -4,5 +4,20 @@
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);
+ },
+
+ validate: function(frm) {
+ let policy = frm.doc.password_policy;
+ if (policy) {
+ if (policy.includes(' ') || policy.includes('--')) {
+ frappe.msgprint("Password policy cannot contain spaces or simultaneous hyphens. The format will be restructured automatically");
+ }
+ frm.set_value('password_policy', policy.split(new RegExp(" |-", 'g')).filter((token) => token).join('-'));
+ }
}
});
diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.json b/erpnext/hr/doctype/hr_settings/hr_settings.json
index 225785d305..5502ce81e3 100644
--- a/erpnext/hr/doctype/hr_settings/hr_settings.json
+++ b/erpnext/hr/doctype/hr_settings/hr_settings.json
@@ -1,5 +1,6 @@
{
"allow_copy": 0,
+ "allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
@@ -13,10 +14,12 @@
"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_settings",
"fieldtype": "Section Break",
"hidden": 0,
@@ -43,12 +46,14 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"description": "Enter retirement age in years",
+ "fetch_if_empty": 0,
"fieldname": "retirement_age",
"fieldtype": "Data",
"hidden": 0,
@@ -76,12 +81,14 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Naming Series",
"description": "Employee record is created using selected field. ",
+ "fetch_if_empty": 0,
"fieldname": "emp_created_by",
"fieldtype": "Select",
"hidden": 0,
@@ -109,11 +116,13 @@
},
{
"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": "leave_approval_notification_template",
"fieldtype": "Link",
"hidden": 0,
@@ -142,10 +151,12 @@
},
{
"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_status_notification_template",
"fieldtype": "Link",
"hidden": 0,
@@ -174,10 +185,12 @@
},
{
"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,
@@ -204,11 +217,13 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Don't send Employee Birthday Reminders",
+ "fetch_if_empty": 0,
"fieldname": "stop_birthday_reminders",
"fieldtype": "Check",
"hidden": 0,
@@ -235,10 +250,12 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "maintain_bill_work_hours_same",
"fieldtype": "Check",
"hidden": 0,
@@ -266,11 +283,13 @@
},
{
"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": "leave_approver_mandatory_in_leave_application",
"fieldtype": "Check",
"hidden": 0,
@@ -298,11 +317,13 @@
},
{
"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": "expense_approver_mandatory_in_expense_claim",
"fieldtype": "Check",
"hidden": 0,
@@ -330,10 +351,13 @@
},
{
"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": "payroll_settings",
"fieldtype": "Section Break",
"hidden": 0,
@@ -360,11 +384,13 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "If checked, Total no. of Working Days will include holidays, and this will reduce the value of Salary Per Day",
+ "fetch_if_empty": 0,
"fieldname": "include_holidays_in_total_working_days",
"fieldtype": "Check",
"hidden": 0,
@@ -391,12 +417,14 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"description": "Emails salary slip to employee based on preferred email selected in Employee",
+ "fetch_if_empty": 0,
"fieldname": "email_salary_slip_to_employee",
"fieldtype": "Check",
"hidden": 0,
@@ -424,10 +452,82 @@
},
{
"allow_bulk_edit": 0,
+ "allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "depends_on": "eval: doc.email_salary_slip_to_employee == 1;",
+ "description": "The salary slip emailed to the employee will be password protected, the password will be generated based on the password policy.",
+ "fetch_if_empty": 0,
+ "fieldname": "encrypt_salary_slips_in_emails",
+ "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": "Encrypt Salary Slips in Emails",
+ "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": "eval: doc.encrypt_salary_slips_in_emails == 1",
+ "description": "Example: SAL-{first_name}-{date_of_birth.year}
This will generate a password like SAL-Jane-1972",
+ "fetch_if_empty": 0,
+ "fieldname": "password_policy",
+ "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": "Password Policy",
+ "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": "max_working_hours_against_timesheet",
"fieldtype": "Float",
"hidden": 0,
@@ -455,10 +555,12 @@
},
{
"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_settings",
"fieldtype": "Section Break",
"hidden": 0,
@@ -481,14 +583,17 @@
"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": "show_leaves_of_all_department_members_in_calendar",
"fieldtype": "Check",
"hidden": 0,
@@ -511,22 +616,21 @@
"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-cog",
"idx": 1,
- "image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 0,
- "modified": "2018-05-03 15:36:13.015466",
- "modified_by": "Administrator",
+ "modified": "2019-04-25 15:08:12.983571",
+ "modified_by": "shivam@example.com",
"module": "HR",
"name": "HR Settings",
"owner": "Administrator",
@@ -553,9 +657,9 @@
],
"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_seen": 0,
+ "track_views": 0
}
\ No newline at end of file
diff --git a/erpnext/hr/doctype/hr_settings/hr_settings.py b/erpnext/hr/doctype/hr_settings/hr_settings.py
index 964eaee7ed..78095b3086 100644
--- a/erpnext/hr/doctype/hr_settings/hr_settings.py
+++ b/erpnext/hr/doctype/hr_settings/hr_settings.py
@@ -5,11 +5,21 @@
from __future__ import unicode_literals
import frappe
+from frappe import _
from frappe.model.document import Document
class HRSettings(Document):
def validate(self):
+ self.set_naming_series()
+ self.validate_password_policy()
+
+ def set_naming_series(self):
from erpnext.setup.doctype.naming_series.naming_series import set_by_naming_series
set_by_naming_series("Employee", "employee_number",
self.get("emp_created_by")=="Naming Series", hide_name_field=True)
+
+ def validate_password_policy(self):
+ 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"))
\ No newline at end of file
diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.json b/erpnext/hr/doctype/payroll_entry/payroll_entry.json
index 562b999b82..d51684f651 100644
--- a/erpnext/hr/doctype/payroll_entry/payroll_entry.json
+++ b/erpnext/hr/doctype/payroll_entry/payroll_entry.json
@@ -21,6 +21,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "section_break0",
"fieldtype": "Section Break",
"hidden": 0,
@@ -53,6 +54,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "column_break0",
"fieldtype": "Column Break",
"hidden": 0,
@@ -86,6 +88,7 @@
"collapsible": 0,
"columns": 0,
"default": "Today",
+ "fetch_if_empty": 0,
"fieldname": "posting_date",
"fieldtype": "Date",
"hidden": 0,
@@ -120,6 +123,7 @@
"columns": 0,
"default": "",
"depends_on": "eval:doc.salary_slip_based_on_timesheet == 0",
+ "fetch_if_empty": 0,
"fieldname": "payroll_frequency",
"fieldtype": "Select",
"hidden": 0,
@@ -153,6 +157,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "column_break1",
"fieldtype": "Column Break",
"hidden": 0,
@@ -186,6 +191,7 @@
"collapsible": 0,
"columns": 0,
"default": "",
+ "fetch_if_empty": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
@@ -220,6 +226,7 @@
"collapsible": 0,
"collapsible_depends_on": "",
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "section_break_8",
"fieldtype": "Section Break",
"hidden": 0,
@@ -252,6 +259,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "branch",
"fieldtype": "Link",
"hidden": 0,
@@ -285,6 +293,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "department",
"fieldtype": "Link",
"hidden": 0,
@@ -318,6 +327,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "column_break_10",
"fieldtype": "Column Break",
"hidden": 0,
@@ -349,6 +359,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "designation",
"fieldtype": "Link",
"hidden": 0,
@@ -382,6 +393,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "number_of_employees",
"fieldtype": "Int",
"hidden": 0,
@@ -414,6 +426,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "sec_break20",
"fieldtype": "Section Break",
"hidden": 0,
@@ -445,6 +458,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "employees",
"fieldtype": "Table",
"hidden": 0,
@@ -478,6 +492,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "section_break_13",
"fieldtype": "Section Break",
"hidden": 0,
@@ -509,6 +524,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "validate_attendance",
"fieldtype": "Check",
"hidden": 0,
@@ -541,6 +557,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "attendance_detail_html",
"fieldtype": "HTML",
"hidden": 0,
@@ -572,6 +589,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "section_break_12",
"fieldtype": "Section Break",
"hidden": 0,
@@ -604,6 +622,7 @@
"collapsible": 0,
"columns": 0,
"default": "0",
+ "fetch_if_empty": 0,
"fieldname": "salary_slip_based_on_timesheet",
"fieldtype": "Check",
"hidden": 0,
@@ -637,6 +656,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "select_payroll_period",
"fieldtype": "Section Break",
"hidden": 0,
@@ -670,6 +690,7 @@
"collapsible": 0,
"columns": 0,
"default": "",
+ "fetch_if_empty": 0,
"fieldname": "start_date",
"fieldtype": "Date",
"hidden": 0,
@@ -703,6 +724,7 @@
"collapsible": 0,
"columns": 0,
"default": "",
+ "fetch_if_empty": 0,
"fieldname": "end_date",
"fieldtype": "Date",
"hidden": 0,
@@ -735,6 +757,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "column_break_11",
"fieldtype": "Column Break",
"hidden": 0,
@@ -766,6 +789,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "deduct_tax_for_unclaimed_employee_benefits",
"fieldtype": "Check",
"hidden": 0,
@@ -798,6 +822,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "deduct_tax_for_unsubmitted_tax_exemption_proof",
"fieldtype": "Check",
"hidden": 0,
@@ -830,6 +855,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "section_break_16",
"fieldtype": "Section Break",
"hidden": 0,
@@ -864,6 +890,7 @@
"columns": 0,
"default": ":Company",
"fetch_from": "",
+ "fetch_if_empty": 0,
"fieldname": "cost_center",
"fieldtype": "Link",
"hidden": 0,
@@ -897,6 +924,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "column_break_18",
"fieldtype": "Column Break",
"hidden": 0,
@@ -928,6 +956,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "project",
"fieldtype": "Link",
"hidden": 0,
@@ -961,6 +990,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "column_break2",
"fieldtype": "Column Break",
"hidden": 0,
@@ -993,6 +1023,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "account",
"fieldtype": "Section Break",
"hidden": 0,
@@ -1026,6 +1057,7 @@
"collapsible": 0,
"columns": 0,
"description": "Select Payment Account to make Bank Entry",
+ "fetch_if_empty": 0,
"fieldname": "payment_account",
"fieldtype": "Link",
"hidden": 0,
@@ -1059,6 +1091,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "section_break2",
"fieldtype": "Section Break",
"hidden": 0,
@@ -1090,6 +1123,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
@@ -1122,6 +1156,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "salary_slips_created",
"fieldtype": "Check",
"hidden": 1,
@@ -1154,6 +1189,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "fetch_if_empty": 0,
"fieldname": "salary_slips_submitted",
"fieldtype": "Check",
"hidden": 1,
@@ -1181,17 +1217,16 @@
}
],
"has_web_view": 0,
- "hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-cog",
"idx": 0,
- "image_view": 0,
"in_create": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2019-02-05 10:41:08.865842",
+ "menu_index": 0,
+ "modified": "2019-03-26 16:55:04.158800",
"modified_by": "Administrator",
"module": "HR",
"name": "Payroll Entry",
@@ -1210,7 +1245,7 @@
"permlevel": 0,
"print": 0,
"read": 1,
- "report": 0,
+ "report": 1,
"role": "HR Manager",
"set_user_permissions": 0,
"share": 1,
@@ -1220,7 +1255,6 @@
],
"quick_entry": 0,
"read_only": 0,
- "read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py
index fbfa3af6cd..5f6ddfc8fc 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.py
@@ -520,13 +520,20 @@ class SalarySlip(TransactionBase):
def email_salary_slip(self):
receiver = frappe.db.get_value("Employee", self.employee, "prefered_email")
+ hr_settings = frappe.get_single("HR Settings")
+ message = "Please see attachment"
+ password = None
+ if hr_settings.encrypt_salary_slips_in_emails:
+ password = generate_password_for_pdf(hr_settings.password_policy, self.employee)
+ message += """
Note: Your salary slip is password protected,
+ the password to unlock the PDF is of the format {0}. """.format(hr_settings.password_policy)
if receiver:
email_args = {
"recipients": [receiver],
- "message": _("Please see attachment"),
+ "message": _(message),
"subject": 'Salary Slip - from {0} to {1}'.format(self.start_date, self.end_date),
- "attachments": [frappe.attach_print(self.doctype, self.name, file_name=self.name)],
+ "attachments": [frappe.attach_print(self.doctype, self.name, file_name=self.name, password=password)],
"reference_doctype": self.doctype,
"reference_name": self.name
}
@@ -843,3 +850,7 @@ def unlink_ref_doc_from_salary_slip(ref_no):
for ss in linked_ss:
ss_doc = frappe.get_doc("Salary Slip", ss)
frappe.db.set_value("Salary Slip", ss_doc.name, "journal_entry", "")
+
+def generate_password_for_pdf(policy_template, employee):
+ employee = frappe.get_doc("Employee", employee)
+ return policy_template.format(**employee.as_dict())
diff --git a/erpnext/hr/doctype/training_event/training_event.py b/erpnext/hr/doctype/training_event/training_event.py
index 1b13b70f5b..5064f03308 100644
--- a/erpnext/hr/doctype/training_event/training_event.py
+++ b/erpnext/hr/doctype/training_event/training_event.py
@@ -5,9 +5,19 @@
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
+from frappe import _
+from frappe.utils import time_diff_in_seconds
from erpnext.hr.doctype.employee.employee import get_employee_emails
class TrainingEvent(Document):
def validate(self):
+ self.set_employee_emails()
+ self.validate_period()
+
+ def set_employee_emails(self):
self.employee_emails = ', '.join(get_employee_emails([d.employee
for d in self.employees]))
+
+ def validate_period(self):
+ if time_diff_in_seconds(self.end_time, self.start_time) <= 0:
+ frappe.throw(_('End time cannot be before start time'))
\ No newline at end of file
diff --git a/erpnext/hr/report/bank_remittance/__init__.py b/erpnext/hr/report/bank_remittance/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/erpnext/hr/report/bank_remittance/bank_remittance.js b/erpnext/hr/report/bank_remittance/bank_remittance.js
new file mode 100644
index 0000000000..1e10f24301
--- /dev/null
+++ b/erpnext/hr/report/bank_remittance/bank_remittance.js
@@ -0,0 +1,28 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Bank Remittance"] = {
+ "filters": [
+ {
+ "fieldname":"company",
+ "label": __("Company"),
+ "fieldtype": "Link",
+ "options": "Company",
+ "default": frappe.defaults.get_user_default("Company"),
+ "reqd": 1
+ },
+ {
+ fieldname:"from_date",
+ label: __("From Date"),
+ fieldtype: "Date",
+ },
+ {
+ fieldname:"to_date",
+ label: __("To Date"),
+ fieldtype: "Date",
+ },
+
+ ]
+}
+
diff --git a/erpnext/hr/report/bank_remittance/bank_remittance.json b/erpnext/hr/report/bank_remittance/bank_remittance.json
new file mode 100644
index 0000000000..5a6228e398
--- /dev/null
+++ b/erpnext/hr/report/bank_remittance/bank_remittance.json
@@ -0,0 +1,25 @@
+{
+ "add_total_row": 0,
+ "creation": "2019-03-26 16:57:52.558895",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 0,
+ "is_standard": "Yes",
+ "letter_head": "Gadgets International",
+ "modified": "2019-03-26 16:57:52.558895",
+ "modified_by": "Administrator",
+ "module": "HR",
+ "name": "Bank Remittance",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Payroll Entry",
+ "report_name": "Bank Remittance",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "HR Manager"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/hr/report/bank_remittance/bank_remittance.py b/erpnext/hr/report/bank_remittance/bank_remittance.py
new file mode 100644
index 0000000000..b2d2c53024
--- /dev/null
+++ b/erpnext/hr/report/bank_remittance/bank_remittance.py
@@ -0,0 +1,154 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.utils import formatdate
+import itertools
+from frappe import _, get_all
+
+def execute(filters=None):
+ columns = [
+ {
+ "label": _("Payroll Number"),
+ "fieldtype": "Link",
+ "fieldname": "payroll_no",
+ "options": "Payroll Entry",
+ "width": 150
+ },
+ {
+ "label": _("Debit A/C Number"),
+ "fieldtype": "Int",
+ "fieldname": "debit_account",
+ "hidden": 1,
+ "width": 200
+ },
+ {
+ "label": _("Payment Date"),
+ "fieldtype": "Data",
+ "fieldname": "payment_date",
+ "width": 100
+ },
+ {
+ "label": _("Employee Name"),
+ "fieldtype": "Link",
+ "fieldname": "employee_name",
+ "options": "Employee",
+ "width": 200
+ },
+ {
+ "label": _("Bank Name"),
+ "fieldtype": "Data",
+ "fieldname": "bank_name",
+ "width": 50
+ },
+ {
+ "label": _("Employee A/C Number"),
+ "fieldtype": "Int",
+ "fieldname": "employee_account_no",
+ "width": 50
+ },
+ {
+ "label": _("IFSC Code"),
+ "fieldtype": "Data",
+ "fieldname": "bank_code",
+ "width": 100
+ },
+ {
+ "label": _("Currency"),
+ "fieldtype": "Data",
+ "fieldname": "currency",
+ "width": 50
+ },
+ {
+ "label": _("Net Salary Amount"),
+ "fieldtype": "Currency",
+ "options": "currency",
+ "fieldname": "amount",
+ "width": 100
+ }
+ ]
+ data = []
+
+ accounts = get_bank_accounts()
+ payroll_entries = get_payroll_entries(accounts, filters)
+ salary_slips = get_salary_slips(payroll_entries)
+ get_emp_bank_ifsc_code(salary_slips)
+
+ for salary in salary_slips:
+ if salary.bank_name and salary.bank_account_no and salary.debit_acc_no and salary.status in ["Submitted", "Paid"]:
+ row = {
+ "payroll_no": salary.payroll_entry,
+ "debit_account": salary.debit_acc_no,
+ "payment_date": frappe.utils.formatdate(salary.modified.strftime('%Y-%m-%d')),
+ "bank_name": salary.bank_name,
+ "employee_account_no": salary.bank_account_no,
+ "bank_code": salary.ifsc_code,
+ "employee_name": salary.employee+": " + salary.employee_name,
+ "currency": frappe.get_cached_value('Company', filters.company, 'default_currency'),
+ "amount": salary.net_pay,
+ }
+ data.append(row)
+ return columns, data
+
+def get_bank_accounts():
+ accounts = [d.name for d in get_all("Account", filters={"account_type": "Bank"})]
+ return accounts
+
+def get_payroll_entries(accounts, filters):
+ payroll_filter = [
+ ('payment_account', 'IN', accounts),
+ ('number_of_employees', '>', 0),
+ ('Company', '=', filters.company)
+ ]
+ if filters.to_date:
+ payroll_filter.append(('posting_date', '<', filters.to_date))
+
+ if filters.from_date:
+ payroll_filter.append(('posting_date', '>', filters.from_date))
+
+ entries = get_all("Payroll Entry", payroll_filter, ["name", "payment_account"])
+
+ payment_accounts = [d.payment_account for d in entries]
+ set_company_account(payment_accounts, entries)
+ return entries
+
+def get_salary_slips(payroll_entries):
+ payroll = [d.name for d in payroll_entries]
+ salary_slips = get_all("Salary Slip", filters = [("payroll_entry", "IN", payroll)],
+ fields = ["modified", "net_pay", "bank_name", "bank_account_no", "payroll_entry", "employee", "employee_name", "status"]
+ )
+
+ payroll_entry_map = {}
+ for entry in payroll_entries:
+ payroll_entry_map[entry.name] = entry
+
+ # appending company debit accounts
+ for slip in salary_slips:
+ slip["debit_acc_no"] = payroll_entry_map[slip.payroll_entry]['company_account']
+
+ return salary_slips
+
+def get_emp_bank_ifsc_code(salary_slips):
+ emp_names = [d.employee for d in salary_slips]
+ ifsc_codes = get_all("Employee", [("name", "IN", emp_names)], ["ifsc_code", "name"])
+
+ ifsc_codes_map = {}
+ for code in ifsc_codes:
+ ifsc_codes_map[code.name] = code
+
+ for slip in salary_slips:
+ slip["ifsc_code"] = ifsc_codes_map[code.name]['ifsc_code']
+
+ return salary_slips
+
+def set_company_account(payment_accounts, payroll_entries):
+ company_accounts = get_all("Bank Account", [("account", "in", payment_accounts)], ["account", "bank_account_no"])
+ company_accounts_map = {}
+ for acc in company_accounts:
+ company_accounts_map[acc.account] = acc
+
+ for entry in payroll_entries:
+ entry["company_account"] = company_accounts_map[entry.payment_account]['bank_account_no']
+
+ return payroll_entries
diff --git a/erpnext/manufacturing/notification/__init__.py b/erpnext/manufacturing/notification/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/erpnext/manufacturing/notification/material_request_receipt_notification/__init__.py b/erpnext/manufacturing/notification/material_request_receipt_notification/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/erpnext/manufacturing/notification/material_request_receipt_notification/material_request_receipt_notification.json b/erpnext/manufacturing/notification/material_request_receipt_notification/material_request_receipt_notification.json
new file mode 100644
index 0000000000..5391a31497
--- /dev/null
+++ b/erpnext/manufacturing/notification/material_request_receipt_notification/material_request_receipt_notification.json
@@ -0,0 +1,29 @@
+{
+ "attach_print": 0,
+ "channel": "Email",
+ "condition": "doc.status == \"Received\" or doc.status == \"Partially Received\"",
+ "creation": "2019-04-29 11:53:23.981418",
+ "days_in_advance": 0,
+ "docstatus": 0,
+ "doctype": "Notification",
+ "document_type": "Material Request",
+ "enabled": 1,
+ "event": "Value Change",
+ "idx": 0,
+ "is_standard": 1,
+ "message": "Material Request Type: {{ doc.material_request_type }}
\nCompany: {{ doc.company }}\n\n
Item Name | \nReceived Quantity | \n
---|---|
{{ item.item_code }} | \n{{ frappe.utils.flt(item.received_qty, 2) }} | \n
Item Name | +Received Quantity | +
---|---|
{{ item.item_code }} | +{{ frappe.utils.flt(item.received_qty, 2) }} | +
{{_("The page you are looking for is missing. This could be because it is moved or there is a typo in the link.")}}
+ +{{ _("Error Code: {0}").format('404') }}
+ +{% endif %} {% endblock %} \ No newline at end of file