diff --git a/.travis.yml b/.travis.yml index 0b3b49feed..beab4e119a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,8 @@ services: - mysql install: + - pip install flake8==3.3.0 + - flake8 . --count --select=E901,E999,F821,F822,F823 --show-source --statistics - sudo rm /etc/apt/sources.list.d/docker.list - sudo apt-get purge -y mysql-common mysql-server mysql-client - nvm install v7.10.0 diff --git a/README.md b/README.md index 1fae1ce2ea..b44a246bb1 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,7 @@ Includes: Accounting, Inventory, Manufacturing, CRM, Sales, Purchase, Project Ma ERPNext is built on the [Frappe](https://github.com/frappe/frappe) Framework, a full-stack web app framework in Python & JavaScript. -- [User Guide](https://frappe.github.io/erpnext/) -- [Getting Help](http://erpnext.org/getting-help.html) +- [User Guide](https://erpnext.org/docs/user) - [Discussion Forum](https://discuss.erpnext.com/) --- diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 73fdf6f6f9..cce1e6fff9 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -20,36 +20,36 @@ def get_fiscal_year(date=None, fiscal_year=None, label="Date", verbose=1, compan def get_fiscal_years(transaction_date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False): fiscal_years = frappe.cache().hget("fiscal_years", company) or [] - - if not fiscal_years: + + if not fiscal_years: # if year start date is 2012-04-01, year end date should be 2013-03-31 (hence subdate) cond = "" if fiscal_year: cond += " and fy.name = {0}".format(frappe.db.escape(fiscal_year)) if company: cond += """ - and (not exists (select name - from `tabFiscal Year Company` fyc - where fyc.parent = fy.name) - or exists(select company - from `tabFiscal Year Company` fyc - where fyc.parent = fy.name + and (not exists (select name + from `tabFiscal Year Company` fyc + where fyc.parent = fy.name) + or exists(select company + from `tabFiscal Year Company` fyc + where fyc.parent = fy.name and fyc.company=%(company)s) ) """ fiscal_years = frappe.db.sql(""" - select - fy.name, fy.year_start_date, fy.year_end_date - from + select + fy.name, fy.year_start_date, fy.year_end_date + from `tabFiscal Year` fy - where + where disabled = 0 {0} - order by + order by fy.year_start_date desc""".format(cond), { "company": company }, as_dict=True) - + frappe.cache().hset("fiscal_years", company, fiscal_years) if transaction_date: @@ -60,10 +60,10 @@ def get_fiscal_years(transaction_date=None, fiscal_year=None, label="Date", verb if fiscal_year and fy.name == fiscal_year: matched = True - if (transaction_date and getdate(fy.year_start_date) <= transaction_date + if (transaction_date and getdate(fy.year_start_date) <= transaction_date and getdate(fy.year_end_date) >= transaction_date): matched = True - + if matched: if as_dict: return (fy,) @@ -158,7 +158,6 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company return flt(bal) def get_count_on(account, fieldname, date): - cond = [] if date: cond.append("posting_date <= '%s'" % frappe.db.escape(cstr(date))) @@ -195,11 +194,6 @@ def get_count_on(account, fieldname, date): select name from `tabAccount` ac where ac.name = gle.account and ac.lft >= %s and ac.rgt <= %s )""" % (acc.lft, acc.rgt)) - - # If group and currency same as company, - # always return balance based on debit and credit in company currency - if acc.account_currency == frappe.db.get_value("Company", acc.company, "default_currency"): - in_account_currency = False else: cond.append("""gle.account = "%s" """ % (frappe.db.escape(account, percent=False), )) @@ -227,7 +221,7 @@ def get_count_on(account, fieldname, date): WHERE docstatus < 2 and posting_date <= %(date)s and against_voucher = %(voucher_no)s and party = %(party)s and name != %(name)s""" .format(select_fields), - {"date": date, "voucher_no": gle.voucher_no, + {"date": date, "voucher_no": gle.voucher_no, "party": gle.party, "name": gle.name})[0][0] outstanding_amount = flt(gle.get(dr_or_cr)) - flt(gle.get(cr_or_dr)) - payment_amount @@ -274,7 +268,7 @@ def add_cc(args=None): if not args: args = frappe.local.form_dict - + args.doctype = "Cost Center" args = make_tree_args(**args) @@ -534,18 +528,18 @@ def get_stock_and_account_difference(account_list=None, posting_date=None): account_balance = get_balance_on(account_data.get('account'), posting_date, in_account_currency=False) stock_value = get_stock_value_on(warehouse, posting_date) if abs(flt(stock_value) - flt(account_balance)) > 0.005: - difference.setdefault(account, flt(stock_value) - flt(account_balance)) + difference.setdefault(account_data.get('account'), flt(stock_value) - flt(account_balance)) return difference -def get_currency_precision(): +def get_currency_precision(): precision = cint(frappe.db.get_default("currency_precision")) if not precision: number_format = frappe.db.get_default("number_format") or "#,###.##" precision = get_number_format_info(number_format)[2] - + return precision - + def get_stock_rbnb_difference(posting_date, company): stock_items = frappe.db.sql_list("""select distinct item_code from `tabStock Ledger Entry` where company=%s""", company) @@ -654,7 +648,7 @@ def get_companies(): @frappe.whitelist() def get_children(): from erpnext.accounts.report.financial_statements import sort_root_accounts - + args = frappe.local.form_dict doctype, company = args['doctype'], args['company'] fieldname = frappe.db.escape(doctype.lower().replace(' ','_')) @@ -695,9 +689,6 @@ def get_children(): return acc -def create_payment_gateway_account(gateway): - create_payment_gateway_account(gateway) - def create_payment_gateway_account(gateway): from erpnext.setup.setup_wizard.setup_wizard import create_bank_account diff --git a/erpnext/change_log/v6/v6_13_1.md b/erpnext/change_log/v6/v6_13_1.md index 1f0a5684d1..4b2c4a9cca 100644 --- a/erpnext/change_log/v6/v6_13_1.md +++ b/erpnext/change_log/v6/v6_13_1.md @@ -1 +1 @@ -- [ERPNext Manual in German](http://frappe.github.io/erpnext/user/manual/de/) contributed by [CWT Connector & Wire Technology GmbH](http://www.cwt-assembly.com/) +- [ERPNext Manual in German](http://erpnext.org/docs/user/manual/de/) contributed by [CWT Connector & Wire Technology GmbH](http://www.cwt-assembly.com/) diff --git a/erpnext/change_log/v6/v6_2_0.md b/erpnext/change_log/v6/v6_2_0.md index 004e90caa4..f4da651c7b 100644 --- a/erpnext/change_log/v6/v6_2_0.md +++ b/erpnext/change_log/v6/v6_2_0.md @@ -1 +1 @@ -- **[Multi-currency Accounting](https://frappe.github.io/erpnext/user/guides/accounts/multi-currency-accounting)**: You can now have an Account in a different currency than your Company's currency +- **[Multi-currency Accounting](https://frappe.io/docs/user/guides/accounts/multi-currency-accounting)**: You can now have an Account in a different currency than your Company's currency diff --git a/erpnext/config/docs.py b/erpnext/config/docs.py index 2d2cc0a892..7c91455a0e 100644 --- a/erpnext/config/docs.py +++ b/erpnext/config/docs.py @@ -1,12 +1,11 @@ from __future__ import unicode_literals -docs_version = "7.x.x" - source_link = "https://github.com/frappe/erpnext" docs_base_url = "https://frappe.github.io/erpnext" -headline = "ERPNext Documentation" -sub_heading = "Detailed explanation for all ERPNext features and developer API" -long_description = """ERPNext is a fully featured ERP system designed for Small and Medium Sized +headline = "ERP Made Simple" +sub_heading = "ERPNext User Guides and API References" +long_description = """ +ERPNext is a fully featured ERP system designed for Small and Medium Sized business. ERPNext covers a wide range of features including Accounting, CRM, Inventory management, Selling, Purchasing, Manufacturing, Projects, HR & Payroll, Website, E-Commerce and much more. @@ -17,7 +16,15 @@ to extend ERPNext functionality. ERPNext is Open Source under the GNU General Public Licence v3 and has been listed as one of the Best Open Source Softwares in the world by many online -blogs.""" +blogs. + +### Getting Started + +To install ERPNext on a server, you will need to install [Frappe Bench](https://github.com/frappe/bench). + +You can also start a free 30 day trial at [https://erpnext.com](https://erpnext.com) + +""" splash_light_background = True google_analytics_id = 'UA-8911157-22' diff --git a/erpnext/docs/assets/img/users-and-permissions/user-permission-company.png b/erpnext/docs/assets/img/users-and-permissions/user-permission-company.png deleted file mode 100644 index eb529f1792..0000000000 Binary files a/erpnext/docs/assets/img/users-and-permissions/user-permission-company.png and /dev/null differ diff --git a/erpnext/docs/assets/img/users-and-permissions/user-permission-quotation.png b/erpnext/docs/assets/img/users-and-permissions/user-permission-quotation.png deleted file mode 100644 index ee7a19b51a..0000000000 Binary files a/erpnext/docs/assets/img/users-and-permissions/user-permission-quotation.png and /dev/null differ diff --git a/erpnext/docs/assets/img/users-and-permissions/user-permissions-company-role-all.png b/erpnext/docs/assets/img/users-and-permissions/user-permissions-company-role-all.png deleted file mode 100644 index 0aececd0d6..0000000000 Binary files a/erpnext/docs/assets/img/users-and-permissions/user-permissions-company-role-all.png and /dev/null differ diff --git a/erpnext/docs/assets/img/users-and-permissions/user-permissions-ignore-user-permissions.png b/erpnext/docs/assets/img/users-and-permissions/user-permissions-ignore-user-permissions.png deleted file mode 100644 index 5f55e37b40..0000000000 Binary files a/erpnext/docs/assets/img/users-and-permissions/user-permissions-ignore-user-permissions.png and /dev/null differ diff --git a/erpnext/docs/assets/img/users-and-permissions/user-permissions-lead-role-permissions.png b/erpnext/docs/assets/img/users-and-permissions/user-permissions-lead-role-permissions.png deleted file mode 100644 index 23564fafe6..0000000000 Binary files a/erpnext/docs/assets/img/users-and-permissions/user-permissions-lead-role-permissions.png and /dev/null differ diff --git a/erpnext/docs/assets/img/users-and-permissions/user-permissions-new.gif b/erpnext/docs/assets/img/users-and-permissions/user-permissions-new.gif deleted file mode 100644 index 609bf257e2..0000000000 Binary files a/erpnext/docs/assets/img/users-and-permissions/user-permissions-new.gif and /dev/null differ diff --git a/erpnext/docs/assets/img/users-and-permissions/user-permissions-quotation-sales-user.png b/erpnext/docs/assets/img/users-and-permissions/user-permissions-quotation-sales-user.png deleted file mode 100644 index dd9ed49d68..0000000000 Binary files a/erpnext/docs/assets/img/users-and-permissions/user-permissions-quotation-sales-user.png and /dev/null differ diff --git a/erpnext/docs/assets/img/users-and-permissions/user-perms/ignore-user-permissions.png b/erpnext/docs/assets/img/users-and-permissions/user-perms/ignore-user-permissions.png new file mode 100644 index 0000000000..56e0d44bcf Binary files /dev/null and b/erpnext/docs/assets/img/users-and-permissions/user-perms/ignore-user-permissions.png differ diff --git a/erpnext/docs/assets/img/users-and-permissions/user-perms/new-user-permission.png b/erpnext/docs/assets/img/users-and-permissions/user-perms/new-user-permission.png new file mode 100644 index 0000000000..d2762dd7b6 Binary files /dev/null and b/erpnext/docs/assets/img/users-and-permissions/user-perms/new-user-permission.png differ diff --git a/erpnext/docs/assets/img/users-and-permissions/user-perms/permitted-documents.png b/erpnext/docs/assets/img/users-and-permissions/user-perms/permitted-documents.png new file mode 100644 index 0000000000..2558b62d01 Binary files /dev/null and b/erpnext/docs/assets/img/users-and-permissions/user-perms/permitted-documents.png differ diff --git a/erpnext/docs/assets/img/users-and-permissions/user-perms/select-document-types.png b/erpnext/docs/assets/img/users-and-permissions/user-perms/select-document-types.png new file mode 100644 index 0000000000..b54d36ac87 Binary files /dev/null and b/erpnext/docs/assets/img/users-and-permissions/user-perms/select-document-types.png differ diff --git a/erpnext/docs/assets/img/users-and-permissions/user-perms/view-selected-documents.png b/erpnext/docs/assets/img/users-and-permissions/user-perms/view-selected-documents.png new file mode 100644 index 0000000000..bc2ce7854b Binary files /dev/null and b/erpnext/docs/assets/img/users-and-permissions/user-perms/view-selected-documents.png differ diff --git a/erpnext/docs/user/manual/de/setting-up/users-and-permissions/user-permissions.md b/erpnext/docs/user/manual/de/setting-up/users-and-permissions/user-permissions.md index a7516936e7..68fc4128cc 100644 --- a/erpnext/docs/user/manual/de/setting-up/users-and-permissions/user-permissions.md +++ b/erpnext/docs/user/manual/de/setting-up/users-and-permissions/user-permissions.md @@ -1,45 +1,5 @@ # Benutzer-Berechtigungen -Beigetragen von CWT Connector & Wire Technology GmbH -Verwenden Sie den Benutzerberechtigungen-Manager um den Zugriff eines Benutzers auf eine Menge von Dokumenten einzuschränken. +This document has been changed but not yet translated. Please see the English Version -Rollenbasierte Berechtigungen definieren den Rahmen an Dokumententypen, innerhalb derer sich ein Benutzer mit einer Anzahl von Rollen bewegen darf. Sie können jedoch noch feinere Einstellungen treffen, wenn Sie für einen Benutzer Benutzerberechtigungen definieren. Wenn Sie bestimmte Dokumente in der Liste der Benutzerberechtigungen eintragen, dann können Sie den Zugriff dieses Benutzers auf bestimmte Dokumente eines bestimmten DocTypes begrenzen, unter der Bedingung, dass die Option "Benutzerberechtigungen anwenden" im Rollenberechtigungs-Manager aktiviert ist. - -Beginnen Sie wie folgt: - -> Einstellungen > Berechtigungen > Benutzerrechte-Manager - - -Abbildung: Übersicht aus dem Benutzerberechtigungs-Manager die aufzeigt, wie Benutzer nur auf bestimmte Firmen zugreifen können - -#### Beispiel - -Der Benutzer "aromn@example.com" hat die Rolle "Nutzer Vertrieb" und wir möchten die Zugriffsrechte des Benutzers so einschränken, dass er nur auf Datensätze einer bestimmten Firma, nämlich der Wind Power LLC, zugreifen kann. - -1\. Wir fügen eine Benutzerberechtigungs-Zeile für die Firma hinzu. - -Abbildung: Hinzufügen einer Zeile "Benutzer-Berechtigung" für die Kombination aus dem Benutzer "aromn@example.com" und der Firma Wind Power LLC - -2\. Die Rolle "Alle" hat nur Leseberechtigungen für die Firma, "Benutzer-Berechtigungen anwenden" ist aktiviert. - -Abbildung: Leseberechtigung mit aktivierter Option "Benutzer-Berechtigungen anwenden" für den DocType Firma - -3\. Die oben abgebildete Kombination der zwei Regeln führt dazu, dass der Benutzer "aromn@example.com" für die Firma Wind Power LLC nur Leserechte hat. - -Abbildung: Der Zugriff wird auf die Firma Wind Power LLC beschränkt - -4\. Wir möchten nun diese Benutzer-Berechtigung für "Firma" auf andere Dokumente wie "Angebot", "Kundenauftrag" etc. übertragen. Diese Formulare haben **Verknüpfungsfelder zu "Firma"**. Als Ergebnis werden Benutzer-Berechtigungen von "Firma" auch auf diese Dokumente übertragen, was dazu führt, dass der Benutzer "aromn@example.com" auf diese Dokumente zugreifen kann, wenn Sie mit Wind Power LLC verbunden sind. - -Abbildung: Benutzer mit der Rolle "Nutzer Vertrieb" können, basierend auf Ihren Benutzer-Berechtigungen, Angebote lesen, schreiben, erstellen, übertragen und stornieren, wenn "Benutzer-Berechtigungen anwenden" aktiviert ist. - -Abbildung: Die Auflistung der Angebote enthält nur Ergebnisse für die Firma Wind Power LLC für den Benutzer "aromn@example.com" - -5\. Benutzer-Berechtigungen werden automatisch auf Basis von verknüpften Feldern angewandt, genauso wie wir es bei den Angeboten gesehen haben. Aber: Das Lead-Formular hat vier Verknüpfungsfelder: "Region", "Firma", "Eigentümer des Leads" und "Nächster Kontakt durch". Nehmen wir an, Sie möchten dass die Leads den Zugriff des Benutzers basierend auf Ihrer Region einschränken, obwohl Sie für die DocTypes "Benutzer", "Region" und "Firma" Benutzer-Berechtigungen angelegt haben. Dann gehen Sie wir folgt vor: Aktivieren Sie die Option "Benutzer-Berechtigungen ignorieren" für die Verknüpfungsfelder "Firma", "Eigentümer des Leads" und "Nächster Kontakt durch". - -Abbildung: Der Vertriebsmitarbeiter kann Leads lesen, schreiben und erstellen, eingeschränkt durch Benutzer-Berechtigungen. - -Abbildung: Markieren Sie "Benutzer-Berechtigungen ignorieren" für die Felder "Firma", "Lead-Inhaber" und "Nächster Kontakt durch" über Setup > Anpassen > Formular anpassen > Lead. - -Abbildung: Aufgrund der obigen Kombination kann der Benutzer "aromn@example.com" nur auf Leads der Region "United States" zugreifen. - -{next} +User Permission diff --git a/erpnext/docs/user/manual/en/customize-erpnext/articles/making-custom-reports-in-erpnext.md b/erpnext/docs/user/manual/en/customize-erpnext/articles/making-custom-reports-in-erpnext.md index 7bc965b656..3d5174fcde 100644 --- a/erpnext/docs/user/manual/en/customize-erpnext/articles/making-custom-reports-in-erpnext.md +++ b/erpnext/docs/user/manual/en/customize-erpnext/articles/making-custom-reports-in-erpnext.md @@ -12,12 +12,12 @@ Report Builder is an in-built report customization tool in ERPNext. This allows Query Report is written in SQL which pull values from account's database and fetch in the report. Though SQL queries can be written from front end, like HTML, its restricted in hosted users. Because it will allow users with no access to specific report to query data directly from the database. -Check Purchase Order Item to be Received report in Stock module for example of Query report. Click [here](https://frappe.github.io/frappe/user/en/guides/reports-and-printing/how-to-make-query-report.html) to learn how to create Query Report. +Check Purchase Order Item to be Received report in Stock module for example of Query report. Click [here](https://frappe.io/docs/user/en/guides/reports-and-printing/how-to-make-query-report.html) to learn how to create Query Report. ### 3. Script Report Script Reports are written in Python and stored on server side. These are complex reports which involves logic and calculation. Since these reports are written on server side, customizing it from hosted account is not possible. -Check Financial Analytics report in Accounts module for example of Script Report. Click [here](https://frappe.github.io/frappe/user/en/guides/reports-and-printing/how-to-make-script-reports.html) to learn how to create Script Report. +Check Financial Analytics report in Accounts module for example of Script Report. Click [here](https://frappe.io/docs/user/en/guides/reports-and-printing/how-to-make-script-reports.html) to learn how to create Script Report. \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/customize-erpnext/customize-form.md b/erpnext/docs/user/manual/en/customize-erpnext/customize-form.md index f52cee1aab..dd14a431db 100644 --- a/erpnext/docs/user/manual/en/customize-erpnext/customize-form.md +++ b/erpnext/docs/user/manual/en/customize-erpnext/customize-form.md @@ -1,5 +1,5 @@ -Before we venture to learn form customization tool, click [here](https://frappe.github.io/frappe/user/en/tutorial/doctypes.html) to understand the architecture of forms in ERPNext. It shall help you in using Customize Form tool more efficiently. +Before we venture to learn form customization tool, click [here](https://frappe.io/docs/user/en/tutorial/doctypes.html) to understand the architecture of forms in ERPNext. It shall help you in using Customize Form tool more efficiently. Customize Form is the tool which allows user to customize property of the standard fields, and insert [custom fields]({{docs_base_url}}/user/manual/en/customize-erpnext/custom-field.html) as per the requirement. Let's assume we need to set Project Name field as a mandatory field in the Sales Order form. Following are the steps which shall be followed to achieve this. diff --git a/erpnext/docs/user/manual/en/introduction/getting-started-with-erpnext.md b/erpnext/docs/user/manual/en/introduction/getting-started-with-erpnext.md index 7e783f679b..2f59eb4e53 100644 --- a/erpnext/docs/user/manual/en/introduction/getting-started-with-erpnext.md +++ b/erpnext/docs/user/manual/en/introduction/getting-started-with-erpnext.md @@ -1,3 +1,7 @@ + + +# Getting Started with ERPNext + There are many ways to get started with ERPNext. ### 1\. See the Demo diff --git a/erpnext/docs/user/manual/en/selling/quotation.md b/erpnext/docs/user/manual/en/selling/quotation.md index 1f7b06c45e..324a6661f0 100644 --- a/erpnext/docs/user/manual/en/selling/quotation.md +++ b/erpnext/docs/user/manual/en/selling/quotation.md @@ -83,6 +83,6 @@ Print Heading. While making your sales transactions like a Quotation (or Sales Order) you can also give discounts to your customers. In the Discount section, add -the discount in percentage or fixed amount. Read [Discount](https://frappe.github.io/erpnext/user/manual/en/selling/articles/applying-discount) for more explanation. +the discount in percentage or fixed amount. Read [Discount](https://erpnext.org/docs/user/manual/en/selling/articles/applying-discount) for more explanation. {next} diff --git a/erpnext/docs/user/manual/en/setting-up/articles/integrating-erpnext-with-other-application.md b/erpnext/docs/user/manual/en/setting-up/articles/integrating-erpnext-with-other-application.md index 3cbf159c2f..7ab6849898 100644 --- a/erpnext/docs/user/manual/en/setting-up/articles/integrating-erpnext-with-other-application.md +++ b/erpnext/docs/user/manual/en/setting-up/articles/integrating-erpnext-with-other-application.md @@ -2,6 +2,6 @@ For now, ERPNext has out-of-the-box integration available for some applications like Shopify, your SMS gateway and payment gateway. To integrate ERPNext with other application, you can use REST API of Frappe. Check following links to learn more about REST API of Frappe. -[Frappe Rest API](https://frappe.github.io/frappe/user/en/guides/integration/rest_api.html) +[Frappe Rest API](https://frappe.io/docs/user/en/guides/integration/rest_api.html) \ No newline at end of file diff --git a/erpnext/docs/user/manual/en/setting-up/users-and-permissions/user-permissions.md b/erpnext/docs/user/manual/en/setting-up/users-and-permissions/user-permissions.md index 7fae95cbe7..f90cf44f99 100644 --- a/erpnext/docs/user/manual/en/setting-up/users-and-permissions/user-permissions.md +++ b/erpnext/docs/user/manual/en/setting-up/users-and-permissions/user-permissions.md @@ -1,60 +1,52 @@ # User Permissions -Role Base Permissions define the periphery of document types within which a user with a set of Roles can move around in. However, you can have an even finer control by defining User Permissions for a User. By setting specific documents in User Permissions list, you can limit access for that User to specific documents of a particular DocType, on the condition that "Apply User Permissions" is checked in Role Permissions Manager. +Along with Role based permissions, you can also set user level permissions that are based on rules that are evaluated against the data containted in the document being accessed. This is particularly useful when you want to restrict based on: -To start with, go to: +1. Allow user to access data belonging to one Company +1. Allow user to access data related to a specific Customer or Territory -> Setup > Permissions > User Permissions Manager +### Creating User Permissions -User Permissions Manager displaying how users can access only a specific Company. +To create a User Permission, go to Setup > Permission > User Permissions -#### Example +When you create a new record you will have to specify -User 'tom.hagen@riosolutions.com' has Sales User role and we want to limit the user to access records for only a specific Company 'Rio Solutions'. +1. The user for which the rule has to be applied +1. The type of document which will be allowed (for example "Company") +1. The specific item that you want to allow (the name of the "Company) - 1. We add a User Permissions row for Company. - - User Permissions For Company +Creating a new user permission - Add User Permissions row for a combination of User 'tom.hagen@riosolutions.com' and Company 'Rio Solutions'. +If you want to apply the permissions to all Roles for that user, keep the "Apply Permissions for all Roles of this User" checked. If you check this, it will automatically setup the rules for Roles to check for User Permissions. - 1. Also Role "All" has only Read permission for Company, with 'Apply User Permissions' checked. - - Role Permissions for All on Company +### Choosing Which Roles to Apply - Read Permission with Apply User Permissions checked for DocType Company. +You can also manually edit the the roles for which you want the user permissions to apply. To do that go the the **Role Permission Manager** and select the role for which you want to Edit the User Permissions. - 1. The combined effect of the above two rules lead to User 'tom.hagen@riosolutions.com' having only Read access to Company 'Rio Solutions'. - - Effect of Role and User Permissions on Company - - Access is limited to Company 'Rio Solutions'. +Note that the "Apply User Permissions" is already checked for this role. Then click on "Select Document Types" - 1. We want this User Permission on Company to get applied on other documents like Quotation, Sales Order, etc. - - These forms have a **Link Field based on Company**. As a result, User Permissions on Company also get applied on these documents, which leads to User 'tom.hagen@riosolutions' to acces these documents having Company 'Rio Solutions'. +Select Document Types to Edit the Setting - Sales User Role Permissions for Quotation - - Users with Sales User Role can Read, Write, Create, Submit and Cancel Quotations based on their User Permissions, since 'Apply User Permissions' is checked. +Here you will see that Company has already been checked. If you want user permissions not be applied for that particular rule, you can un check it. - Quotation List limited to results for Company 'Rio Solutions' +Select Document Types to Edit the Setting - Quotation List is limited to results for Company 'Rio Solutions' for User 'tom.hagen@riosolutions.com'. +### Ignoring User Permissions on Certain Fields - 1. User Permissions get applied automatically based on Link Fields, just like how it worked for Quotation. But, Lead Form has 4 Link fields: Territory, Company, Lead Owner and Next Contact By. Say, you want Leads to limit access to Users based only on Territory, even though you have defined User Permissions for DocTypes User, Territory and Company. You can do this by setting 'Ignore User Permissions' for Link fields: Company, Lead Owner and Next Contact By. - -Role Permissions on Lead for Sales User Role +Another way of allowing documents to be seen that have been restricited by User Permissions is to check "Ignore User Permissions" on a particular field by going to **Customize Form** -Sales User can Read, Write and Create Leads limited by User Permissions. +For example you don't want Assets to be restricited for any user, then select **Asset** in **Customize Form** and in the Company field, check on "Ignore User Permissions" -Set Ingore User Permissions from Setup > Customize > Customize Form +Ignore User Permissions on specific properties -Check 'Ingore User Permissions' for Company, Lead Owner and Next Contact By fields using Setup > Customize > Customize Form for Lead. +### Strict Permisssions -Lead List is limited to records with Territory 'United States' +Since User Permissions are applied via Roles, there may be many users belonging to a particular Role. Suppose you have three users belonging to Role "Accounts User" and you have applied **User Permissions** to only one user, then the permissions will only be restricted to that user. -Due to the effect of the above combination, User 'tom.hagen@riosolutions.com' can only access Leads with Territory 'United States'. +You can change this setting incase you want the user permissions to be assigned to all users, even if they are not assigned any user permissions by going to **System Settings** and checking "Apply Strict User Permissions" -{next} +### Checking How User Permissions are Applied +Finally once you have created your air-tight permission model, and you want to check how it applies to various users, you can see it via the **Permitted Documents for User** report. Using this report, you can select the **User** and document type and check how user permissions get applied. + +Permitted Documents for User report diff --git a/erpnext/hr/doctype/attendance/test_attendance.js b/erpnext/hr/doctype/attendance/test_attendance.js index 044d7162b5..82347ad567 100644 --- a/erpnext/hr/doctype/attendance/test_attendance.js +++ b/erpnext/hr/doctype/attendance/test_attendance.js @@ -3,15 +3,8 @@ QUnit.module('hr'); QUnit.test("Test: Attendance [HR]", function (assert) { assert.expect(4); let done = assert.async(); - let employee_code; frappe.run_serially([ - // get employee's auto generated name - () => frappe.set_route("List", "Employee", "List"), - () => frappe.timeout(0.5), - () => frappe.click_link('Test Employee'), - () => frappe.timeout(0.5), - () => employee_code = frappe.get_route()[2], // test attendance creation for one employee () => frappe.set_route("List", "Attendance", "List"), () => frappe.timeout(0.5), @@ -21,7 +14,8 @@ QUnit.test("Test: Attendance [HR]", function (assert) { "Form for new Attendance opened successfully."), // set values in form () => cur_frm.set_value("company", "Test Company"), - () => cur_frm.set_value("employee", employee_code), + () => frappe.db.get_value('Employee', {'employee_name':'Test Employee 1'}, 'name'), + (employee) => cur_frm.set_value("employee", employee.message.name), () => cur_frm.save(), () => frappe.timeout(1), // check docstatus of attendance before submit [Draft] diff --git a/erpnext/hr/doctype/employee/test_employee.js b/erpnext/hr/doctype/employee/test_employee.js index 77d1433f0e..64a6b7a9bf 100644 --- a/erpnext/hr/doctype/employee/test_employee.js +++ b/erpnext/hr/doctype/employee/test_employee.js @@ -10,7 +10,7 @@ QUnit.test("Test: Employee [HR]", function (assert) { () => frappe.set_route("List", "Employee", "List"), () => frappe.new_doc("Employee"), () => frappe.timeout(1), - () => cur_frm.set_value("employee_name", "Test Employee"), + () => cur_frm.set_value("employee_name", "Test Employee 1"), () => cur_frm.set_value("salutation", "Ms"), () => cur_frm.set_value("company", "Test Company"), () => cur_frm.set_value("date_of_joining", frappe.datetime.add_months(today_date, -2)), // joined 2 month from now @@ -26,7 +26,7 @@ QUnit.test("Test: Employee [HR]", function (assert) { () => cur_frm.save(), () => frappe.timeout(1), // check name of employee - () => assert.equal("Test Employee", cur_frm.doc.employee_name, + () => assert.equal("Test Employee 1", cur_frm.doc.employee_name, 'name of employee correctly saved'), // check auto filled gender according to salutation () => assert.equal("Female", cur_frm.doc.gender, diff --git a/erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js b/erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js index c7cd3a39ab..a71ba0f2a2 100644 --- a/erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js +++ b/erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js @@ -3,29 +3,46 @@ QUnit.module('hr'); QUnit.test("Test: Employee attendance tool [HR]", function (assert) { assert.expect(3); let done = assert.async(); - let attendance_date = frappe.datetime.add_days(frappe.datetime.nowdate(), -1); // previous day + let today_date = frappe.datetime.nowdate(); + let date_of_attendance = frappe.datetime.add_days(today_date, -1); // previous day frappe.run_serially([ + // create employee + () => { + return frappe.tests.make('Employee', [ + {salutation: "Mr"}, + {employee_name: "Test Employee 2"}, + {company: "Test Company"}, + {date_of_joining: frappe.datetime.add_months(today_date, -2)}, // joined 2 month from now + {date_of_birth: frappe.datetime.add_months(today_date, -240)}, // age is 20 years + {employment_type: "Test Employment type"}, + {holiday_list: "Test Holiday list"}, + {branch: "Test Branch"}, + {department: "Test Department"}, + {designation: "Test Designation"} + ]); + }, () => frappe.set_route("Form", "Employee Attendance Tool"), () => frappe.timeout(0.5), () => assert.equal("Employee Attendance Tool", cur_frm.doctype, "Form for Employee Attendance Tool opened successfully."), // set values in form - () => cur_frm.set_value("date", attendance_date), + () => cur_frm.set_value("date", date_of_attendance), () => cur_frm.set_value("branch", "Test Branch"), () => cur_frm.set_value("department", "Test Department"), () => cur_frm.set_value("company", "Test Company"), - () => frappe.timeout(0.5), - () => frappe.click_check('Test Employee'), - () => frappe.tests.click_button('Mark Present'), + () => frappe.timeout(1), + () => frappe.click_button('Check all'), + () => frappe.click_button('Mark Present'), // check if attendance is marked () => frappe.set_route("List", "Attendance", "List"), () => frappe.timeout(1), () => { - assert.equal("Test Employee", cur_list.data[0].employee_name, - "attendance marked correctly saved"); - assert.equal(attendance_date, cur_list.data[0].attendance_date, - "attendance date is set correctly"); + assert.deepEqual(["Test Employee 2", "Test Employee 1"], [cur_list.data[0].employee_name, cur_list.data[1].employee_name], + "marked attendance correctly saved for both employee"); + let marked_attendance = cur_list.data.filter(d => d.attendance_date == date_of_attendance); + assert.equal(marked_attendance.length, 2, + 'both the attendance are marked for correct date'); }, () => done() ]); diff --git a/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js new file mode 100644 index 0000000000..5d189d2cf2 --- /dev/null +++ b/erpnext/hr/doctype/leave_allocation/test_leave_allocation.js @@ -0,0 +1,37 @@ +QUnit.module('hr'); + +QUnit.test("Test: Leave allocation [HR]", function (assert) { + assert.expect(3); + let done = assert.async(); + let today_date = frappe.datetime.nowdate(); + + frappe.run_serially([ + // test creating leave alloction + () => frappe.set_route("List", "Leave Allocation", "List"), + () => frappe.new_doc("Leave Allocation"), + () => frappe.timeout(1), + () => frappe.db.get_value('Employee', {'employee_name':'Test Employee 1'}, 'name'), + (employee) => cur_frm.set_value("employee", employee.message.name), + () => cur_frm.set_value("leave_type", "Test Leave type"), + () => cur_frm.set_value("to_date", frappe.datetime.add_months(today_date, 2)), // for two months + () => cur_frm.set_value("description", "This is just for testing"), + () => cur_frm.set_value("new_leaves_allocated", 2), + () => frappe.click_check('Add unused leaves from previous allocations'), + // save form + () => cur_frm.save(), + () => frappe.timeout(1), + () => cur_frm.savesubmit(), + () => frappe.timeout(1), + () => assert.equal("Confirm", cur_dialog.title, + 'confirmation for leave alloction shown'), + () => frappe.click_button('Yes'), + () => frappe.timeout(1), + // check auto filled from date + () => assert.equal(today_date, cur_frm.doc.from_date, + "from date correctly set"), + // check for total leaves + () => assert.equal(cur_frm.doc.carry_forwarded_leaves + 2, cur_frm.doc.total_leaves_allocated, + "total leave calculation is correctly set"), + () => done() + ]); +}); \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js b/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js new file mode 100644 index 0000000000..e71ff6e396 --- /dev/null +++ b/erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js @@ -0,0 +1,36 @@ +QUnit.module('hr'); + +QUnit.test("Test: Leave control panel [HR]", function (assert) { + assert.expect(2); + let done = assert.async(); + let today_date = frappe.datetime.nowdate(); + + frappe.run_serially([ + // test leave allocation using leave control panel + () => frappe.set_route("Form", "Leave Control Panel"), + () => frappe.timeout(1), + () => cur_frm.set_value("leave_type", "Test Leave type"), + () => cur_frm.set_value("company", "Test Company"), + () => cur_frm.set_value("employment_type", "Test Employment Type"), + () => cur_frm.set_value("branch", "Test Branch"), + () => cur_frm.set_value("department", "Test Department"), + () => cur_frm.set_value("designation", "Test Designation"), + () => cur_frm.set_value("from_date", frappe.datetime.add_months(today_date, -2)), + () => cur_frm.set_value("to_date", frappe.datetime.add_days(today_date, -1)), // for two months [not today] + () => cur_frm.set_value("no_of_days", 3), + // allocate leaves + () => frappe.click_button('Allocate'), + () => frappe.timeout(1), + () => assert.equal("Message", cur_dialog.title, + "leave alloction message shown"), + () => frappe.click_button('Close'), + () => frappe.set_route("List", "Leave Allocation", "List"), + () => frappe.timeout(1), + () => { + let leave_allocated = cur_list.data.filter(d => d.leave_type == "Test Leave type"); + assert.equal(2, leave_allocated.length, + 'leave allocation successfully done for both the employee'); + }, + () => done() + ]); +}); \ No newline at end of file diff --git a/erpnext/hr/doctype/leave_type/test_leave_type.js b/erpnext/hr/doctype/leave_type/test_leave_type.js new file mode 100644 index 0000000000..4cde49bce3 --- /dev/null +++ b/erpnext/hr/doctype/leave_type/test_leave_type.js @@ -0,0 +1,22 @@ +QUnit.module('hr'); + +QUnit.test("Test: Leave type [HR]", function (assert) { + assert.expect(1); + let done = assert.async(); + + frappe.run_serially([ + // test leave type creation + () => frappe.set_route("List", "Leave Type", "List"), + () => frappe.new_doc("Leave Type"), + () => frappe.timeout(1), + () => cur_frm.set_value("leave_type_name", "Test Leave type"), + () => cur_frm.set_value("max_days_allowed", "5"), + () => frappe.click_check('Is Carry Forward'), + // save form + () => cur_frm.save(), + () => frappe.timeout(1), + () => assert.equal("Test Leave type", cur_frm.doc.leave_type_name, + 'leave type correctly saved'), + () => done() + ]); +}); \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/production_planning_tool/test_production_planning_tool.py b/erpnext/manufacturing/doctype/production_planning_tool/test_production_planning_tool.py index 4f80b6a626..af4220ccfc 100644 --- a/erpnext/manufacturing/doctype/production_planning_tool/test_production_planning_tool.py +++ b/erpnext/manufacturing/doctype/production_planning_tool/test_production_planning_tool.py @@ -15,7 +15,7 @@ test_records = frappe.get_test_records('Production Planning Tool') test_dependencies = ["Item","BOM"] class TestEvent(unittest.TestCase): - + def test_materials_requests_all_raw_multi_level(self): items = ["_Test PPT Item Raw A","_Test PPT Item Raw B","_Test PPT Item Raw C","_Test PPT Item Raw D", "_Test PPT Item Sub A","_Test PPT Item Sub B","_Test PPT Item Sub C","_Test PPT Item SC A", @@ -23,10 +23,10 @@ class TestEvent(unittest.TestCase): quantities = [14,9,36,1,0,0,0,0,0,0] types = ["Purchase","Purchase","Purchase","Purchase","Manufacture","Manufacture","Manufacture","Purchase", "Purchase","Manufacture"] - + self.runtest_materials_requests(items, quantities, types, use_multi_level_bom=1, only_raw_materials=1, \ include_subcontracted=1) - + def test_materials_requests_multi_no_subcontracted(self): items = ["_Test PPT Item Raw A","_Test PPT Item Raw B","_Test PPT Item Raw C","_Test PPT Item Raw D", "_Test PPT Item Sub A","_Test PPT Item Sub B","_Test PPT Item Sub C","_Test PPT Item SC A", @@ -34,12 +34,12 @@ class TestEvent(unittest.TestCase): quantities = [14,5,20,0,0,0,0,0,0,0] types = ["Purchase","Purchase","Purchase","Purchase","Manufacture","Manufacture","Manufacture","Purchase", "Purchase","Manufacture"] - + # This one should fail for now self.runtest_materials_requests(items, quantities, types, use_multi_level_bom=1, only_raw_materials=1, \ include_subcontracted=0) - + def test_materials_requests_manufacture_and_sub_multi_level(self): items = ["_Test PPT Item Raw A","_Test PPT Item Raw B","_Test PPT Item Raw C","_Test PPT Item Raw D", @@ -48,10 +48,10 @@ class TestEvent(unittest.TestCase): quantities = [14,9,36,1,2,5,2,1,4,0] types = ["Purchase","Purchase","Purchase","Purchase","Manufacture","Manufacture","Manufacture","Purchase", "Purchase","Manufacture"] - + self.runtest_materials_requests(items, quantities, types, use_multi_level_bom=1, only_raw_materials=0, \ include_subcontracted=1) - + def test_materials_requests_manufacture_multi_level(self): items = ["_Test PPT Item Raw A","_Test PPT Item Raw B","_Test PPT Item Raw C","_Test PPT Item Raw D", "_Test PPT Item Sub A","_Test PPT Item Sub B","_Test PPT Item Sub C","_Test PPT Item SC A", @@ -59,12 +59,12 @@ class TestEvent(unittest.TestCase): quantities = [14,5,20,0,2,5,2,1,4,0] types = ["Purchase","Purchase","Purchase","Purchase","Manufacture","Manufacture","Manufacture","Purchase", "Purchase","Manufacture"] - + self.runtest_materials_requests(items, quantities, types, use_multi_level_bom=1, only_raw_materials=0, \ include_subcontracted=0) - - - + + + def test_materials_requests_single_level_purch_only(self): items = ["_Test PPT Item Raw A","_Test PPT Item Raw B","_Test PPT Item Raw C","_Test PPT Item Raw D", "_Test PPT Item Sub A","_Test PPT Item Sub B","_Test PPT Item Sub C","_Test PPT Item SC A", @@ -72,10 +72,10 @@ class TestEvent(unittest.TestCase): quantities = [2,0,0,0,0,0,0,1,0,0] types = ["Purchase","Purchase","Purchase","Purchase","Manufacture","Manufacture","Manufacture","Purchase", "Purchase","Manufacture"] - + self.runtest_materials_requests(items, quantities, types, use_multi_level_bom=0, only_raw_materials=1, \ include_subcontracted=0) - + def test_materials_requests_single_level(self): items = ["_Test PPT Item Raw A","_Test PPT Item Raw B","_Test PPT Item Raw C","_Test PPT Item Raw D", "_Test PPT Item Sub A","_Test PPT Item Sub B","_Test PPT Item Sub C","_Test PPT Item SC A", @@ -83,32 +83,32 @@ class TestEvent(unittest.TestCase): quantities = [2,0,0,0,2,1,0,1,0,0] types = ["Purchase","Purchase","Purchase","Purchase","Manufacture","Manufacture","Manufacture","Purchase", "Purchase","Manufacture"] - + self.runtest_materials_requests(items, quantities, types, use_multi_level_bom=0, only_raw_materials=0, \ include_subcontracted=0) - + def runtest_materials_requests(self, items, quantities, types,use_multi_level_bom, only_raw_materials, \ include_subcontracted): - + clear_material_requests() create_test_records() - - ppt = run_production_planning_tool(use_multi_level_bom=use_multi_level_bom, - only_raw_materials=only_raw_materials, include_subcontracted=include_subcontracted, - item_code = "_Test PPT Item Master", bom_no = "BOM-_Test PPT Item Master-001", - planned_qty = 1, planned_start_date = "5/5/2029", + + ppt = run_production_planning_tool(use_multi_level_bom=use_multi_level_bom, + only_raw_materials=only_raw_materials, include_subcontracted=include_subcontracted, + item_code = "_Test PPT Item Master", bom_no = "BOM-_Test PPT Item Master-001", + planned_qty = 1, planned_start_date = "5/5/2029", warehouse = "_Test Warehouse - _TC", company = "_Test Company") - + create_material_requests(ppt) - + for item, qty, type in zip(items, quantities, types): self.assertEqual(qty, get_requested_qty(item)) for mat_req_type in get_requested_types(item): - self.assertEqual(type, mat_req_type) - + self.assertEqual(type, mat_req_type) + def create_test_records(): from erpnext.stock.doctype.item.test_item import make_item - + subA = make_item("_Test PPT Item Sub A",{ "item_code": "_Test PPT Item Sub A", "item_name": "_Test PPT Item Sub A", @@ -119,7 +119,7 @@ def create_test_records(): "stock_uom": "_Test UOM", "item_group": "_Test Item Group", "default_warehouse": "_Test Warehouse - _TC"}) - + subB = make_item("_Test PPT Item Sub B",{ "item_code": "_Test PPT Item Sub B", "item_name": "_Test PPT Item Sub B", @@ -130,7 +130,7 @@ def create_test_records(): "stock_uom": "_Test UOM", "item_group": "_Test Item Group", "default_warehouse": "_Test Warehouse - _TC"}) - + subC = make_item("_Test PPT Item Sub C",{ "item_code": "_Test PPT Item Sub C", "item_name": "_Test PPT Item Sub C", @@ -141,7 +141,7 @@ def create_test_records(): "stock_uom": "_Test UOM", "item_group": "_Test Item Group", "default_warehouse": "_Test Warehouse - _TC"}) - + sCA = make_item("_Test PPT Item SC A",{ "item_code": "_Test PPT Item SC A", "item_name": "_Test PPT Item SC A", @@ -172,7 +172,7 @@ def create_test_records(): "stock_uom": "_Test UOM", "item_group": "_Test Item Group", "default_warehouse": "_Test Warehouse - _TC"}) - + rawA = make_item("_Test PPT Item Raw A",{ "item_code": "_Test PPT Item Raw A", "item_name": "_Test PPT Item Raw A", @@ -183,7 +183,7 @@ def create_test_records(): "stock_uom": "_Test UOM", "item_group": "_Test Item Group", "default_warehouse": "_Test Warehouse - _TC"}) - + rawB = make_item("_Test PPT Item Raw B",{ "item_code": "_Test PPT Item Raw B", "item_name": "_Test PPT Item Raw B", @@ -194,7 +194,7 @@ def create_test_records(): "stock_uom": "_Test UOM", "item_group": "_Test Item Group", "default_warehouse": "_Test Warehouse - _TC"}) - + rawC = make_item("_Test PPT Item Raw C",{ "item_code": "_Test PPT Item Raw C", "item_name": "_Test PPT Item Raw C", @@ -205,7 +205,7 @@ def create_test_records(): "stock_uom": "_Test UOM", "item_group": "_Test Item Group", "default_warehouse": "_Test Warehouse - _TC"}) - + rawD = make_item("_Test PPT Item Raw D",{ "item_code": "_Test PPT Item Raw D", "item_name": "_Test PPT Item Raw D", @@ -216,7 +216,7 @@ def create_test_records(): "stock_uom": "_Test UOM", "item_group": "_Test Item Group", "default_warehouse": "_Test Warehouse - _TC"}) - + master = make_item("_Test PPT Item Master",{ "item_code": "_Test PPT Item Master", "item_name": "_Test PPT Item Master", @@ -227,9 +227,9 @@ def create_test_records(): "stock_uom": "_Test UOM", "item_group": "_Test Item Group", "default_warehouse": "_Test Warehouse - _TC"}) - - - + + + bom_subB = make_bom("BOM-_Test PPT Item Sub B-001",{"quantity":1.0, "item": "_Test PPT Item Sub B", "is_active": 1, @@ -239,39 +239,39 @@ def create_test_records(): "rate":100, "amount": 100, "stock_uom": "_Test UOM"}, {"item_code": "_Test PPT Item Raw C", "doctype":"BOM Item", "stock_qty":4, "rate":100, "amount": 400,"stock_uom": "_Test UOM"}]) - + bom_subC = make_bom("BOM-_Test PPT Item Sub C-001",{"quantity":1, "item": "_Test PPT Item Sub C", "is_active": 1, "is_default": 1, "docstatus": 1, "with_operations": 0}, [ - {"item_code": "_Test PPT Item Raw A","item_name": "_Test PPT Item Raw A", + {"item_code": "_Test PPT Item Raw A","item_name": "_Test PPT Item Raw A", "doctype":"BOM Item", "stock_qty":6, "rate":100, "amount": 600}, - {"item_code": "_Test PPT Item Sub B","item_name": "_Test PPT Item Sub B", + {"item_code": "_Test PPT Item Sub B","item_name": "_Test PPT Item Sub B", "bom_no":"BOM-_Test PPT Item Sub B-001", "doctype":"BOM Item", "stock_qty":2, "rate":100, "amount": 200}]) - + bom_sCA = make_bom("BOM-_Test PPT Item SC A-001",{"quantity":1, "item": "_Test PPT Item SC A", "is_active": 1, "is_default": 1, "docstatus": 1, "with_operations": 0}, [ - {"item_code": "_Test PPT Item Raw D","item_name": "_Test PPT Item Raw D", + {"item_code": "_Test PPT Item Raw D","item_name": "_Test PPT Item Raw D", "doctype":"BOM Item", "stock_qty":1, "rate":100, "amount": 100}]) - + bom_sCB = make_bom("BOM-_Test PPT Item SC B-001",{"quantity":1, "item": "_Test PPT Item SC B", "is_active": 1, "is_default": 1, "docstatus": 1, "with_operations": 0}, [ - {"item_code": "_Test PPT Item Raw B","item_name": "_Test PPT Item Raw B", + {"item_code": "_Test PPT Item Raw B","item_name": "_Test PPT Item Raw B", "doctype":"BOM Item", "stock_qty":1, "rate":100, "amount": 100}, - {"item_code": "_Test PPT Item Raw C","item_name": "_Test PPT Item Raw C", + {"item_code": "_Test PPT Item Raw C","item_name": "_Test PPT Item Raw C", "doctype":"BOM Item", "stock_qty":4, "rate":100, "amount": 400}]) - + bom_subA = make_bom("BOM-_Test PPT Item Sub A-001",{"quantity":1, "item": "_Test PPT Item Sub A", "is_active": 1, @@ -279,29 +279,29 @@ def create_test_records(): "docstatus": 1, "with_operations": 0}, [ {"item_code": "_Test PPT Item Sub C","item_name": "_Test PPT Item Sub C", - "bom_no":"BOM-_Test PPT Item Sub C-001", "doctype":"BOM Item", + "bom_no":"BOM-_Test PPT Item Sub C-001", "doctype":"BOM Item", "stock_qty":1, "rate":100, "amount": 100}, - {"item_code": "_Test PPT Item SC B","item_name": "_Test PPT Item SC B", + {"item_code": "_Test PPT Item SC B","item_name": "_Test PPT Item SC B", "bom_no":"BOM-_Test PPT Item SC B-001", "doctype":"BOM Item", "stock_qty":2, - "rate":100, "amount": 200}]) - + "rate":100, "amount": 200}]) + bom_master = make_bom("BOM-_Test PPT Item Master-001",{"quantity":1, "item": "_Test PPT Item Master", "is_active": 1, "is_default": 1, "docstatus": 1, "with_operations": 0}, [ - {"item_code": "_Test PPT Item Sub A","item_name": "_Test PPT Item Sub A", - "bom_no":"BOM-_Test PPT Item Sub A-001", + {"item_code": "_Test PPT Item Sub A","item_name": "_Test PPT Item Sub A", + "bom_no":"BOM-_Test PPT Item Sub A-001", "doctype":"BOM Item", "stock_qty":2, "rate":100, "amount": 200}, - {"item_code": "_Test PPT Item Sub B","item_name": "_Test PPT Item Sub B", - "bom_no":"BOM-_Test PPT Item Sub B-001", + {"item_code": "_Test PPT Item Sub B","item_name": "_Test PPT Item Sub B", + "bom_no":"BOM-_Test PPT Item Sub B-001", "doctype":"BOM Item", "stock_qty":1, "rate":100, "amount": 100}, - {"item_code": "_Test PPT Item Raw A","item_name": "_Test PPT Item Raw A", + {"item_code": "_Test PPT Item Raw A","item_name": "_Test PPT Item Raw A", "doctype":"BOM Item", "stock_qty":2, "rate":100, "amount": 200}, - {"item_code": "_Test PPT Item SC A","item_name": "_Test PPT Item SC A", - "bom_no":"BOM-_Test PPT Item SC A-001", + {"item_code": "_Test PPT Item SC A","item_name": "_Test PPT Item SC A", + "bom_no":"BOM-_Test PPT Item SC A-001", "doctype":"BOM Item", "stock_qty":1, "rate":100, "amount": 100} ]) @@ -309,7 +309,7 @@ def create_test_records(): def make_bom(name, properties=None, items=None): if frappe.db.exists("BOM", name): return frappe.get_doc("BOM", name) - + bom = frappe.new_doc("BOM") item = frappe.get_doc({ "doctype": "BOM", @@ -317,20 +317,20 @@ def make_bom(name, properties=None, items=None): "quantity": "1", "with_operations": 0 }) - + if properties: bom.update(properties) - + if items: for item in items: bom.append("items", item) - + bom.insert() bom.submit() - + return bom - + def clear_material_requests(): frappe.db.sql("delete from `tabMaterial Request Item`") frappe.db.sql("delete from `tabMaterial Request`") @@ -339,53 +339,50 @@ def clear_material_requests(): def run_production_planning_tool(**args): ppt = frappe.new_doc("Production Planning Tool") args = frappe._dict(args) - + if args.use_multi_level_bom: ppt.use_multi_level_bom = args.use_multi_level_bom else: ppt.use_multi_level_bom = 0 - + if args.only_raw_materials: ppt.only_raw_materials = args.only_raw_materials else: ppt.only_raw_materials = 0 - + if args.include_subcontracted: ppt.include_subcontracted = args.include_subcontracted else: ppt.include_subcontracted = 0 - + if args.warehouse: - ppt.purchase_request_for_warehouse = args.warehouse - + ppt.purchase_request_for_warehouse = args.warehouse + if args.company: ppt.company = args.company ppt.create_material_requests_for_all_required_qty = 1 - - ppt.append("items",{"item_code": args.item_code, "bom_no": args.bom_no, "planned_qty": args.planned_qty, - "planned_start_date": args.planned_start_date, "warehouse": args.warehouse}) - - return ppt -def create_production_orders(ppt): - raise_production_orders(ppt) + ppt.append("items",{"item_code": args.item_code, "bom_no": args.bom_no, "planned_qty": args.planned_qty, + "planned_start_date": args.planned_start_date, "warehouse": args.warehouse}) + + return ppt def create_material_requests(ppt): ppt.raise_material_requests() - + def get_requested_qty(item_code): total_qty = 0 - for d in frappe.db.sql("""select item.qty as qty - from `tabMaterial Request` mat_req, `tabMaterial Request Item` item + for d in frappe.db.sql("""select item.qty as qty + from `tabMaterial Request` mat_req, `tabMaterial Request Item` item where item.item_code = %(item_code)s and item.parent = mat_req.name""", {"item_code":item_code}, as_dict=1): total_qty += d.qty return total_qty def get_requested_types(item_code): types = [] - for d in frappe.db.sql("""select mat_req.material_request_type as type - from `tabMaterial Request` mat_req, `tabMaterial Request Item` item + for d in frappe.db.sql("""select mat_req.material_request_type as type + from `tabMaterial Request` mat_req, `tabMaterial Request Item` item where item.item_code = %(item_code)s and item.parent = mat_req.name""", {"item_code":item_code}, as_dict=1): types.append(d.type) return types - + diff --git a/erpnext/patches/v4_0/apply_user_permissions.py b/erpnext/patches/v4_0/apply_user_permissions.py index 640fe6b92d..c21b605188 100644 --- a/erpnext/patches/v4_0/apply_user_permissions.py +++ b/erpnext/patches/v4_0/apply_user_permissions.py @@ -12,8 +12,6 @@ def execute(): frappe.clear_cache() def update_hr_permissions(): - from frappe.core.page.user_permissions import user_permissions - # add set user permissions rights to HR Manager frappe.db.sql("""update `tabDocPerm` set `set_user_permissions`=1 where parent in ('Employee', 'Leave Application') and role='HR Manager' and permlevel=0 and `read`=1""") diff --git a/erpnext/patches/v4_2/add_currency_turkish_lira.py b/erpnext/patches/v4_2/add_currency_turkish_lira.py index dac1fe908f..1a46089f94 100644 --- a/erpnext/patches/v4_2/add_currency_turkish_lira.py +++ b/erpnext/patches/v4_2/add_currency_turkish_lira.py @@ -6,5 +6,5 @@ import frappe def execute(): return - country = get_country_info(country="Turkey") - add_country_and_currency("Turkey", country) + # country = get_country_info(country="Turkey") + # add_country_and_currency("Turkey", country) diff --git a/erpnext/patches/v4_2/default_website_style.py b/erpnext/patches/v4_2/default_website_style.py index d168c86831..e8f9502ea6 100644 --- a/erpnext/patches/v4_2/default_website_style.py +++ b/erpnext/patches/v4_2/default_website_style.py @@ -3,9 +3,9 @@ import frappe def execute(): return - frappe.reload_doc('website', 'doctype', 'style_settings') - style_settings = frappe.get_doc("Style Settings", "Style Settings") - if not style_settings.apply_style: - style_settings.update(default_properties) - style_settings.apply_style = 1 - style_settings.save() + # frappe.reload_doc('website', 'doctype', 'style_settings') + # style_settings = frappe.get_doc("Style Settings", "Style Settings") + # if not style_settings.apply_style: + # style_settings.update(default_properties) + # style_settings.apply_style = 1 + # style_settings.save() diff --git a/erpnext/patches/v7_0/update_party_status.py b/erpnext/patches/v7_0/update_party_status.py index f3733dbd84..9ca3d02b9d 100644 --- a/erpnext/patches/v7_0/update_party_status.py +++ b/erpnext/patches/v7_0/update_party_status.py @@ -2,19 +2,19 @@ import frappe def execute(): return - for party_type in ('Customer', 'Supplier'): - frappe.reload_doctype(party_type) - - # set all as default status - frappe.db.sql('update `tab{0}` set status=%s'.format(party_type), default_status[party_type]) - - for doctype in status_depends_on[party_type]: - filters = get_filters_for(doctype) - parties = frappe.get_all(doctype, fields="{0} as party".format(party_type.lower()), - filters=filters, limit_page_length=1) - - parties = filter(None, [p.party for p in parties]) - - if parties: - frappe.db.sql('update `tab{0}` set status="Open" where name in ({1})'.format(party_type, - ', '.join(len(parties) * ['%s'])), parties) \ No newline at end of file + # for party_type in ('Customer', 'Supplier'): + # frappe.reload_doctype(party_type) + # + # # set all as default status + # frappe.db.sql('update `tab{0}` set status=%s'.format(party_type), default_status[party_type]) + # + # for doctype in status_depends_on[party_type]: + # filters = get_filters_for(doctype) + # parties = frappe.get_all(doctype, fields="{0} as party".format(party_type.lower()), + # filters=filters, limit_page_length=1) + # + # parties = filter(None, [p.party for p in parties]) + # + # if parties: + # frappe.db.sql('update `tab{0}` set status="Open" where name in ({1})'.format(party_type, + # ', '.join(len(parties) * ['%s'])), parties) \ No newline at end of file diff --git a/erpnext/patches/v8_1/delete_deprecated_reports.py b/erpnext/patches/v8_1/delete_deprecated_reports.py index 887277ab9c..99d77c103c 100644 --- a/erpnext/patches/v8_1/delete_deprecated_reports.py +++ b/erpnext/patches/v8_1/delete_deprecated_reports.py @@ -51,4 +51,4 @@ def check_and_update_auto_email_report(report): frappe.delete_doc("Auto Email Report", auto_email_report) elif report in ["Customer Addresses And Contacts", "Supplier Addresses And Contacts"]: - frapppe.db.set_value("Auto Email Report", auto_email_report, "report", report) \ No newline at end of file + frappe.db.set_value("Auto Email Report", auto_email_report, "report", report) \ No newline at end of file diff --git a/erpnext/projects/doctype/activity_type/activity_type.json b/erpnext/projects/doctype/activity_type/activity_type.json index 65a0fa16ee..dc50a09df7 100644 --- a/erpnext/projects/doctype/activity_type/activity_type.json +++ b/erpnext/projects/doctype/activity_type/activity_type.json @@ -1,5 +1,6 @@ { "allow_copy": 0, + "allow_guest_to_view": 0, "allow_import": 1, "allow_rename": 1, "autoname": "field:activity_type", @@ -12,6 +13,7 @@ "editable_grid": 0, "fields": [ { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -22,7 +24,8 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, - "in_list_view": 0, + "in_global_search": 0, + "in_list_view": 1, "in_standard_filter": 0, "label": "Activity Type", "length": 0, @@ -39,6 +42,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -49,6 +53,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Default Costing Rate", @@ -67,6 +72,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -77,6 +83,7 @@ "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, @@ -94,6 +101,7 @@ "unique": 0 }, { + "allow_bulk_edit": 0, "allow_on_submit": 0, "bold": 0, "collapsible": 0, @@ -104,6 +112,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, + "in_global_search": 0, "in_list_view": 0, "in_standard_filter": 0, "label": "Default Billing Rate", @@ -120,20 +129,51 @@ "search_index": 0, "set_only_once": 0, "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Disabled", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 } ], + "has_web_view": 0, "hide_heading": 0, "hide_toolbar": 0, "icon": "icon-flag", "idx": 1, "image_view": 0, "in_create": 0, - "in_dialog": 0, "is_submittable": 0, "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-12-13 12:38:18.218618", + "modified": "2017-07-25 20:11:05.229092", "modified_by": "Administrator", "module": "Projects", "name": "Activity Type", @@ -149,11 +189,11 @@ "export": 1, "if_owner": 0, "import": 1, - "is_custom": 0, "permlevel": 0, "print": 1, "read": 1, "report": 1, + "restrict": 0, "role": "System Manager", "set_user_permissions": 0, "share": 1, @@ -175,6 +215,7 @@ "print": 1, "read": 1, "report": 1, + "restrict": 0, "role": "Projects User", "set_user_permissions": 0, "share": 1, @@ -196,6 +237,7 @@ "print": 0, "read": 1, "report": 0, + "restrict": 0, "role": "Employee", "set_user_permissions": 0, "share": 0, @@ -206,6 +248,8 @@ "quick_entry": 1, "read_only": 0, "read_only_onload": 0, + "show_name_in_global_search": 0, "sort_order": "ASC", + "track_changes": 0, "track_seen": 0 } \ No newline at end of file diff --git a/erpnext/projects/doctype/activity_type/test_activity_type.js b/erpnext/projects/doctype/activity_type/test_activity_type.js new file mode 100644 index 0000000000..8023121671 --- /dev/null +++ b/erpnext/projects/doctype/activity_type/test_activity_type.js @@ -0,0 +1,20 @@ +QUnit.test("test: Activity Type", function (assert) { + // number of asserts + assert.expect(1); + let done = assert.async(); + + frappe.run_serially([ + // insert a new Activity Type + () => frappe.set_route("List", "Activity Type", "List"), + () => frappe.new_doc("Activity Type"), + () => frappe.timeout(1), + () => frappe.click_link('Edit in full page'), + () => cur_frm.set_value("activity_type", "Test Activity"), + () => frappe.click_button('Save'), + () => frappe.timeout(1), + () => { + assert.equal(cur_frm.doc.name,"Test Activity"); + }, + () => done() + ]); +}); diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index 95fd420ba4..db00cdf18a 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -170,6 +170,7 @@ class Timesheet(Document): for data in self.get('time_logs'): self.check_workstation_timings(data) self.validate_overlap(data) + validate_activity(data) def validate_overlap(self, data): if self.production_order: @@ -270,6 +271,10 @@ class Timesheet(Document): data.billing_amount = data.billing_rate * hours data.costing_amount = data.costing_rate * hours +def validate_activity(data): + if frappe.get_value('Activity Type', data.activity_type, 'disabled'): + frappe.throw(_("Activity type for row {0} is disabled").format(data.idx)) + @frappe.whitelist() def get_projectwise_timesheet_data(project, parent=None): cond = '' diff --git a/erpnext/public/js/help_links.js b/erpnext/public/js/help_links.js index 6ac60c2c30..7de87b7964 100644 --- a/erpnext/public/js/help_links.js +++ b/erpnext/public/js/help_links.js @@ -14,7 +14,7 @@ frappe.help.help_links['List/User'] = [ frappe.help.help_links['permission-manager'] = [ { label: 'Role Permissions Manager', url: 'https://frappe.github.io/erpnext/user/manual/en/setting-up/users-and-permissions/role-based-permissions' }, { label: 'Managing Perm Level in Permissions Manager', url: 'https://frappe.github.io/erpnext/user/manual/en/setting-up/articles/managing-perm-level' }, - { label: 'User Permissions Manager', url: 'https://frappe.github.io/erpnext/user/manual/en/setting-up/users-and-permissions/user-permissions' }, + { label: 'User Permissions', url: 'https://frappe.github.io/erpnext/user/manual/en/setting-up/users-and-permissions/user-permissions' }, { label: 'Sharing', url: 'https://frappe.github.io/erpnext/user/manual/en/setting-up/users-and-permissions/sharing' }, { label: 'Password', url: 'https://frappe.github.io/erpnext/user/manual/en/setting-up/articles/change-password' }, ] diff --git a/erpnext/setup/page/welcome_to_erpnext/welcome_to_erpnext.html b/erpnext/setup/page/welcome_to_erpnext/welcome_to_erpnext.html index cd4d9771c4..57fce28eeb 100644 --- a/erpnext/setup/page/welcome_to_erpnext/welcome_to_erpnext.html +++ b/erpnext/setup/page/welcome_to_erpnext/welcome_to_erpnext.html @@ -22,7 +22,7 @@ diff --git a/erpnext/templates/includes/announcement/announcement_row.html b/erpnext/templates/includes/announcement/announcement_row.html index d807bfc530..3099441e34 100644 --- a/erpnext/templates/includes/announcement/announcement_row.html +++ b/erpnext/templates/includes/announcement/announcement_row.html @@ -3,12 +3,12 @@
-

{{ doc.subject }}

+

{{ doc.subject }}

{{ doc.description }}

{{ doc.posted_by }} - {{ frappe.format_date(doc.modified) }} - {{ doc.num_attachments }} attachments + {{ frappe.format_date(doc.modified) }} + {{ doc.num_attachments }} attachments

diff --git a/erpnext/tests/ui/tests.txt b/erpnext/tests/ui/tests.txt index 9382114d89..f1d961af68 100644 --- a/erpnext/tests/ui/tests.txt +++ b/erpnext/tests/ui/tests.txt @@ -20,6 +20,9 @@ erpnext/hr/doctype/employment_type/test_employment_type.js erpnext/hr/doctype/employee/test_employee.js erpnext/hr/doctype/employee_attendance_tool/test_employee_attendance_tool.js erpnext/hr/doctype/attendance/test_attendance.js +erpnext/hr/doctype/leave_type/test_leave_type.js +erpnext/hr/doctype/leave_control_panel/test_leave_control_panel.js +erpnext/hr/doctype/leave_allocation/test_leave_allocation.js erpnext/schools/doctype/academic_year/test_academic_year.js erpnext/schools/doctype/academic_term/test_academic_term.js erpnext/schools/doctype/school_settings/test_school_settings.js