Merge branch 'develop' of https://github.com/frappe/frappe into website-bootstrap-4
This commit is contained in:
commit
b6d5607ebc
414 changed files with 8766 additions and 7764 deletions
BIN
.github/frappe-bird.png
vendored
BIN
.github/frappe-bird.png
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 7.5 KiB |
BIN
.github/frappe-framework-logo.png
vendored
Normal file
BIN
.github/frappe-framework-logo.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.9 KiB |
|
|
@ -11,17 +11,13 @@ setup_mariadb_env() {
|
|||
if [[ $DB == 'mariadb' ]]; then
|
||||
setup_mariadb_env 'test_frappe'
|
||||
bench --site test_site reinstall --yes
|
||||
bench --site test_site setup-help
|
||||
bench setup-global-help --root_password travis
|
||||
bench --site test_site scheduler disable
|
||||
bench --site test_site run-tests --coverage
|
||||
|
||||
elif [[ $TEST_TYPE == 'ui' ]]; then
|
||||
setup_mariadb_env 'test_site_ui'
|
||||
bench --site test_site_ui --force restore ./apps/frappe/test_sites/test_site_ui/20181116_225029-test_site_ui-database.sql.gz
|
||||
bench --site test_site_ui --force restore ./apps/frappe/test_sites/test_site_ui/test_site_ui-database.sql.gz
|
||||
bench --site test_site_ui migrate
|
||||
bench --site test_site_ui setup-help
|
||||
bench setup-global-help --root_password travis
|
||||
bench --site test_site_ui scheduler disable
|
||||
cd apps/frappe && yarn && yarn cypress:run
|
||||
|
||||
|
|
@ -29,8 +25,6 @@ elif [[ $DB == 'postgres' ]]; then
|
|||
psql -c "CREATE DATABASE test_frappe;" -U postgres
|
||||
psql -c "CREATE USER test_frappe WITH PASSWORD 'test_frappe';" -U postgres
|
||||
bench --site test_site_postgres reinstall --yes
|
||||
bench --site test_site_postgres setup-help
|
||||
bench setup-global-help --db_type=postgres --root_password travis
|
||||
bench --site test_site_postgres scheduler disable
|
||||
bench --site test_site_postgres run-tests --coverage
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<div align="center">
|
||||
<img src=".github/frappe-bird.png" height="150">
|
||||
<img src=".github/frappe-framework-logo.png" height="150">
|
||||
<h1>
|
||||
<a href="https://frappe.io">
|
||||
frappe
|
||||
|
|
@ -22,7 +22,7 @@
|
|||
</a>
|
||||
<a href='https://www.codetriage.com/frappe/frappe'>
|
||||
<img src='https://www.codetriage.com/frappe/frappe/badges/users.svg'>
|
||||
</a>
|
||||
</a>
|
||||
<a href='https://coveralls.io/github/frappe/frappe?branch=develop'>
|
||||
<img src='https://coveralls.io/repos/github/frappe/frappe/badge.svg?branch=develop'>
|
||||
</a>
|
||||
|
|
|
|||
5
cypress/fixtures/example.json
Normal file
5
cypress/fixtures/example.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
context('Login', () => {
|
||||
beforeEach(() => {
|
||||
cy.request('/api/method/logout');
|
||||
cy.visit('/login');
|
||||
cy.location().should('be', '/login');
|
||||
});
|
||||
|
||||
it('greets with login screen', () => {
|
||||
cy.get('.page-card-head').contains('Sign In');
|
||||
cy.get('.page-card-head').contains('Login');
|
||||
});
|
||||
|
||||
it('validates password', () => {
|
||||
|
|
|
|||
50
cypress/integration/relative_filters.js
Normal file
50
cypress/integration/relative_filters.js
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
context('Relative Timeframe', () => {
|
||||
beforeEach(() => {
|
||||
cy.login('Administrator', 'qwe');
|
||||
cy.visit('/desk');
|
||||
});
|
||||
before(() => {
|
||||
cy.login('Administrator', 'qwe');
|
||||
cy.visit('/desk');
|
||||
cy.window().its('frappe').then(frappe => {
|
||||
frappe.call("frappe.tests.test_utils.create_todo_records");
|
||||
});
|
||||
});
|
||||
it('set relative filter for Previous and check list', () => {
|
||||
cy.visit('/desk#List/ToDo/List');
|
||||
cy.get('.list-row:contains("this is fourth todo")').should('exist');
|
||||
cy.get('.tag-filters-area .btn:contains("Add Filter")').click();
|
||||
cy.get('.fieldname-select-area').should('exist');
|
||||
cy.get('.fieldname-select-area input').type("Due Date{enter}", { delay: 100 });
|
||||
cy.get('select.condition.form-control').select("Previous");
|
||||
cy.get('.filter-field select.input-with-feedback.form-control').select("1 week");
|
||||
cy.server();
|
||||
cy.route({
|
||||
method: 'POST',
|
||||
url: '/'
|
||||
}).as('applyFilter');
|
||||
cy.get('.filter-box .btn:contains("Apply")').click();
|
||||
cy.wait('@applyFilter');
|
||||
cy.get('.list-row-container').its('length').should('eq', 1);
|
||||
cy.get('.list-row-container').should('contain', 'this is second todo');
|
||||
cy.get('.remove-filter.btn').click();
|
||||
});
|
||||
it('set relative filter for Next and check list', () => {
|
||||
cy.visit('/desk#List/ToDo/List');
|
||||
cy.get('.list-row:contains("this is fourth todo")').should('exist');
|
||||
cy.get('.tag-filters-area .btn:contains("Add Filter")').click();
|
||||
cy.get('.fieldname-select-area input').type("Due Date{enter}", { delay: 100 });
|
||||
cy.get('select.condition.form-control').select("Next");
|
||||
cy.get('.filter-field select.input-with-feedback.form-control').select("1 week");
|
||||
cy.server();
|
||||
cy.route({
|
||||
method: 'POST',
|
||||
url: '/'
|
||||
}).as('applyFilter');
|
||||
cy.get('.filter-box .btn:contains("Apply")').click();
|
||||
cy.wait('@applyFilter');
|
||||
cy.get('.list-row-container').its('length').should('eq', 1);
|
||||
cy.get('.list-row').should('contain', 'this is first todo');
|
||||
cy.get('.remove-filter.btn').click();
|
||||
});
|
||||
});
|
||||
50
cypress/integration/table_multiselect.js
Normal file
50
cypress/integration/table_multiselect.js
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
context('Table MultiSelect', () => {
|
||||
beforeEach(() => {
|
||||
cy.login('Administrator', 'qwe');
|
||||
});
|
||||
|
||||
it('select value from multiselect dropdown', () => {
|
||||
cy.visit('/desk#Form/ToDo/New ToDo 1');
|
||||
cy.fill_field('description', 'asdf', 'Text Editor').blur();
|
||||
cy.get('input[data-fieldname="assign_to"]').focus().as('input');
|
||||
cy.get('input[data-fieldname="assign_to"] + ul').should('be.visible');
|
||||
cy.get('@input').type('faris{enter}', { delay: 100 });
|
||||
cy.get('.frappe-control[data-fieldname="assign_to"] .form-control .tb-selected-value')
|
||||
.first().as('selected-value');
|
||||
cy.get('@selected-value').should('contain', 'faris@erpnext.com');
|
||||
|
||||
cy.server();
|
||||
cy.route('POST', '/').as('save_form');
|
||||
// trigger save
|
||||
cy.get('.primary-action').click();
|
||||
cy.wait('@save_form').its('status').should('eq', 200);
|
||||
cy.get('@selected-value').should('contain', 'faris@erpnext.com');
|
||||
});
|
||||
|
||||
it('delete value using backspace', () => {
|
||||
cy.visit('/desk#List/ToDo/List');
|
||||
cy.get('.list-row a').should('exist');
|
||||
cy.get('.list-subject').last().find('a').click();
|
||||
cy.get('input[data-fieldname="assign_to"]').focus().type('{backspace}');
|
||||
cy.get('.frappe-control[data-fieldname="assign_to"] .form-control .tb-selected-value')
|
||||
.should('not.exist');
|
||||
});
|
||||
|
||||
it('delete value using x', () => {
|
||||
cy.visit('/desk#List/ToDo/List');
|
||||
cy.get('.list-row a').should('exist');
|
||||
cy.get('.list-subject').last().find('a').click();
|
||||
cy.get('.frappe-control[data-fieldname="assign_to"] .form-control .tb-selected-value').as('existing_value');
|
||||
cy.get('@existing_value').find('.btn-remove').click();
|
||||
cy.get('@existing_value').should('not.exist');
|
||||
});
|
||||
|
||||
it('navigate to selected value', () => {
|
||||
cy.visit('/desk#List/ToDo/List');
|
||||
cy.get('.list-row a').should('exist');
|
||||
cy.get('.list-subject').last().find('a').click();
|
||||
cy.get('.frappe-control[data-fieldname="assign_to"] .form-control .tb-selected-value').as('existing_value');
|
||||
cy.get('@existing_value').find('.btn-link-to-form').click();
|
||||
cy.location('hash').should('contain', 'Form/User/faris@erpnext.com');
|
||||
});
|
||||
});
|
||||
|
|
@ -17,7 +17,13 @@ from faker import Faker
|
|||
from .exceptions import *
|
||||
from .utils.jinja import (get_jenv, get_template, render_template, get_email_from_template, get_jloader)
|
||||
|
||||
__version__ = '10.1.68'
|
||||
# Hamless for Python 3
|
||||
# For Python 2 set default encoding to utf-8
|
||||
if sys.version[0] == '2':
|
||||
reload(sys)
|
||||
sys.setdefaultencoding("utf-8")
|
||||
|
||||
__version__ = '11.1.3'
|
||||
__title__ = "Frappe Framework"
|
||||
|
||||
local = Local()
|
||||
|
|
|
|||
|
|
@ -49,6 +49,10 @@ def clear_defaults_cache(user=None):
|
|||
elif frappe.flags.in_install!="frappe":
|
||||
frappe.cache().delete_key("defaults")
|
||||
|
||||
def clear_document_cache():
|
||||
frappe.local.document_cache = {}
|
||||
frappe.cache().delete_key("document_cache")
|
||||
|
||||
def clear_doctype_cache(doctype=None):
|
||||
cache = frappe.cache()
|
||||
|
||||
|
|
@ -70,7 +74,8 @@ def clear_doctype_cache(doctype=None):
|
|||
|
||||
# clear all parent doctypes
|
||||
|
||||
for dt in frappe.db.get_all('DocField', 'parent', dict(fieldtype='Table', options=doctype)):
|
||||
for dt in frappe.db.get_all('DocField', 'parent',
|
||||
dict(fieldtype=['in', frappe.model.table_fields], options=doctype)):
|
||||
clear_single(dt.parent)
|
||||
|
||||
# clear all notifications
|
||||
|
|
@ -81,3 +86,6 @@ def clear_doctype_cache(doctype=None):
|
|||
for name in groups:
|
||||
cache.delete_value(name)
|
||||
|
||||
# Clear all document's cache. To clear documents of a specific DocType document_cache should be restructured
|
||||
clear_document_cache()
|
||||
|
||||
|
|
|
|||
19
frappe/change_log/v11/v11_1_0.md
Normal file
19
frappe/change_log/v11/v11_1_0.md
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
- Dynamic [Frappe Charts](https://github.com/frappe/charts) with Report Builder (built by @pratu16x7)
|
||||
- New Frappe Chat for easier internal communication (built by @achillesrasquinha)
|
||||
- [Frappe DataTable](https://github.com/frappe/datatable) for better reports (built by @netchampfaris)
|
||||
- Google Calendar can now be integrated with Frappe
|
||||
- Files can now be uploaded using drag-and-drop
|
||||
- Enhanced List View and Tree View
|
||||
- Bulk Actions from List View
|
||||
- Quill editor has been introduced in place of Summernote
|
||||
- HTML Editor has been introduced
|
||||
- New User Permissions
|
||||
- Subscriptions in ERPNext now moved to Auto Repeat in Frappe
|
||||
- Support for Razorpay and PayPal subscriptions
|
||||
- Better Social login, Workflow
|
||||
- Messages for when user goes online/offline
|
||||
- Logout from all sessions on password change
|
||||
- Desktop icons can now be selected from a dialog box
|
||||
- Changes have been made to ensure Frappe is compatible with Python 3
|
||||
- Better documentation is now available with support for more languages
|
||||
- A lot of other fixes have been done to ensure a better overall user experience
|
||||
|
|
@ -1,2 +1,2 @@
|
|||
### User Permissions
|
||||
- User Permission is now a DocType, a new UX for the existing Role and User Permission managers to make it easy to enter permissions. For more details please check <a href="https://erpnext.org/docs/user/manual/en/setting-up/users-and-permissions/user-permissions">User Permissions</a>
|
||||
- User Permission is now a DocType, a new UX for the existing Role and User Permission managers to make it easy to enter permissions. For more details please check <a href="https://erpnext.com/docs/user/manual/en/setting-up/users-and-permissions/user-permissions">User Permissions</a>
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
# imports - standard imports
|
||||
import json
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
# imports - standard imports
|
||||
import unittest
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
# imports - module imports
|
||||
from frappe.model.document import Document
|
||||
from frappe import _
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
# imports - standard imports
|
||||
import unittest
|
||||
|
||||
|
|
@ -16,7 +18,7 @@ class TestChatProfile(unittest.TestCase):
|
|||
# def test_create(self):
|
||||
# with self.assertRaises(frappe.ValidationError):
|
||||
# chat_profile.create(test_user)
|
||||
|
||||
|
||||
# user = get_user_doc(session.user)
|
||||
# if not user.chat_profile:
|
||||
# chat_profile.create(user.name)
|
||||
|
|
@ -47,7 +49,7 @@ class TestChatProfile(unittest.TestCase):
|
|||
# ))
|
||||
|
||||
# user = get_user_doc(session.user)
|
||||
# prev = chat_profile.get(user.name)
|
||||
# prev = chat_profile.get(user.name)
|
||||
|
||||
# chat_profile.update(user.name, data = dict(
|
||||
# status = 'Offline'
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
# imports - standard imports
|
||||
import json
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
# imports - module imports
|
||||
from frappe.model.document import Document
|
||||
import frappe
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
# imports - module imports
|
||||
from frappe.chat.util.util import (
|
||||
get_user_doc,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
# imports - standard imports
|
||||
import unittest
|
||||
|
||||
|
|
@ -7,6 +9,7 @@ from frappe.chat.util import (
|
|||
safe_json_loads
|
||||
)
|
||||
import frappe
|
||||
import six
|
||||
|
||||
class TestChatUtil(unittest.TestCase):
|
||||
def test_safe_json_loads(self):
|
||||
|
|
@ -15,10 +18,10 @@ class TestChatUtil(unittest.TestCase):
|
|||
|
||||
number = safe_json_loads("1.0")
|
||||
self.assertEqual(type(number), float)
|
||||
|
||||
|
||||
string = safe_json_loads("foobar")
|
||||
self.assertEqual(type(string), str)
|
||||
|
||||
self.assertEqual(type(string), six.text_type)
|
||||
|
||||
array = safe_json_loads('[{ "foo": "bar" }]')
|
||||
self.assertEqual(type(array), list)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
# imports - third-party imports
|
||||
import requests
|
||||
|
||||
|
|
@ -26,7 +28,7 @@ def get_user_doc(user = None):
|
|||
|
||||
user = user or session.user
|
||||
user = frappe.get_doc('User', user)
|
||||
|
||||
|
||||
return user
|
||||
|
||||
def squashify(what):
|
||||
|
|
@ -43,9 +45,9 @@ def safe_json_loads(*args):
|
|||
arg = json.loads(arg)
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
|
||||
results.append(arg)
|
||||
|
||||
|
||||
return squashify(results)
|
||||
|
||||
def filter_dict(what, keys, ignore = False):
|
||||
|
|
@ -68,7 +70,7 @@ def get_if_empty(a, b):
|
|||
if not a:
|
||||
a = b
|
||||
return a
|
||||
|
||||
|
||||
def listify(arg):
|
||||
if not isinstance(arg, list):
|
||||
arg = [arg]
|
||||
|
|
@ -89,7 +91,7 @@ def check_url(what, raise_err = False):
|
|||
raise ValueError('{what} not a valid URL.')
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
return True
|
||||
|
||||
def create_test_user(module):
|
||||
|
|
@ -110,5 +112,5 @@ def get_emojis():
|
|||
if resp.ok:
|
||||
emojis = resp.json()
|
||||
redis.hset('frappe_emojis', 'emojis', emojis)
|
||||
|
||||
|
||||
return dictify(emojis)
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.chat.util import filter_dict, safe_json_loads
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from __future__ import unicode_literals, absolute_import, print_function
|
||||
import click
|
||||
import json, sys
|
||||
import sys
|
||||
import frappe
|
||||
from frappe.utils import cint
|
||||
from frappe.commands import pass_context, get_site
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
from __future__ import unicode_literals, absolute_import, print_function
|
||||
import click
|
||||
import frappe
|
||||
from frappe.commands import pass_context, get_site
|
||||
|
||||
# translation
|
||||
|
|
|
|||
|
|
@ -343,6 +343,7 @@ def mariadb(context):
|
|||
frappe.conf.db_name,
|
||||
'-h', frappe.conf.db_host or "localhost",
|
||||
'--pager=less -SFX',
|
||||
'--safe-updates',
|
||||
"-A"])
|
||||
|
||||
@click.command('postgres')
|
||||
|
|
@ -600,58 +601,27 @@ def get_version():
|
|||
@click.option('--db_type')
|
||||
@click.option('--root_password')
|
||||
def setup_global_help(db_type=None, root_password=None):
|
||||
'''setup help table in a separate database that will be
|
||||
'''Deprecated: setup help table in a separate database that will be
|
||||
shared by the whole bench and set `global_help_setup` as 1 in
|
||||
common_site_config.json'''
|
||||
|
||||
from frappe.installer import update_site_config
|
||||
|
||||
frappe.local.flags = frappe._dict()
|
||||
frappe.local.flags.in_setup_help = True
|
||||
frappe.local.flags.in_install = True
|
||||
frappe.local.lang = 'en'
|
||||
frappe.local.conf = frappe.get_site_config(sites_path='.')
|
||||
|
||||
update_site_config('global_help_setup', 1,
|
||||
site_config_path=os.path.join('.', 'common_site_config.json'))
|
||||
|
||||
if root_password:
|
||||
frappe.local.conf.root_password = root_password
|
||||
|
||||
if not frappe.local.conf.db_type:
|
||||
frappe.local.conf.db_type = db_type
|
||||
|
||||
|
||||
from frappe.utils.help import sync
|
||||
sync()
|
||||
print_in_app_help_deprecation()
|
||||
|
||||
@click.command('get-docs-app')
|
||||
@click.argument('app')
|
||||
def get_docs_app(app):
|
||||
'''Get the docs app for given app'''
|
||||
from frappe.utils.help import setup_apps_for_docs
|
||||
setup_apps_for_docs(app)
|
||||
'''Deprecated: Get the docs app for given app'''
|
||||
print_in_app_help_deprecation()
|
||||
|
||||
@click.command('get-all-docs-apps')
|
||||
def get_all_docs_apps():
|
||||
'''Get docs apps for all apps'''
|
||||
from frappe.utils.help import setup_apps_for_docs
|
||||
for app in frappe.get_installed_apps():
|
||||
setup_apps_for_docs(app)
|
||||
'''Deprecated: Get docs apps for all apps'''
|
||||
print_in_app_help_deprecation()
|
||||
|
||||
@click.command('setup-help')
|
||||
@pass_context
|
||||
def setup_help(context):
|
||||
'''Setup help table in the current site (called after migrate)'''
|
||||
from frappe.utils.help import sync
|
||||
|
||||
for site in context.sites:
|
||||
try:
|
||||
frappe.init(site)
|
||||
frappe.connect()
|
||||
sync()
|
||||
finally:
|
||||
frappe.destroy()
|
||||
'''Deprecated: Setup help table in the current site (called after migrate)'''
|
||||
print_in_app_help_deprecation()
|
||||
|
||||
@click.command('rebuild-global-search')
|
||||
@pass_context
|
||||
|
|
@ -715,6 +685,10 @@ def auto_deploy(context, app, migrate=False, restart=False, remote='upstream'):
|
|||
else:
|
||||
print('No Updates')
|
||||
|
||||
def print_in_app_help_deprecation():
|
||||
print("In app help has been removed.\nYou can access the documentation on erpnext.com/docs or frappe.io/docs")
|
||||
return
|
||||
|
||||
commands = [
|
||||
build,
|
||||
clear_cache,
|
||||
|
|
@ -747,6 +721,5 @@ commands = [
|
|||
add_to_email_queue,
|
||||
setup_global_help,
|
||||
setup_help,
|
||||
rebuild_global_search,
|
||||
auto_deploy
|
||||
rebuild_global_search
|
||||
]
|
||||
|
|
|
|||
|
|
@ -80,5 +80,12 @@ def get_data():
|
|||
"color": '#FF4136',
|
||||
'standard': 1,
|
||||
'idx': 15
|
||||
},
|
||||
{
|
||||
"module_name": 'Settings',
|
||||
"color": "#bdc3c7",
|
||||
"reverse": 1,
|
||||
"icon": "octicon octicon-settings",
|
||||
"type": "module"
|
||||
}
|
||||
]
|
||||
|
|
|
|||
51
frappe/config/settings.py
Normal file
51
frappe/config/settings.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
from frappe import _
|
||||
|
||||
def get_data():
|
||||
return [{
|
||||
"label": _("Settings"),
|
||||
"icon": "fa fa-wrench",
|
||||
"items": [
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "System Settings",
|
||||
"label": _("System Settings"),
|
||||
"description": _("Language, Date and Time settings"),
|
||||
"hide_count": True
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Domain Settings",
|
||||
"label": _("Domain Settings"),
|
||||
"description": _("Enable / Disable Domains"),
|
||||
"hide_count": True
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Print Settings",
|
||||
"label": _("Print Settings"),
|
||||
"description": _("Print Style, PDF Size"),
|
||||
"hide_count": True
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Website Settings",
|
||||
"label": _("Website Settings"),
|
||||
"description": _("Landing Page, Website Theme, Brand Setup and more"),
|
||||
"hide_count": True
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "S3 Backup Settings",
|
||||
"label": _("S3 Backup Settings"),
|
||||
"description": _("Enable / Disable Backup, Backup Frequency"),
|
||||
"hide_count": True
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "SMS Settings",
|
||||
"label": _("SMS Settings"),
|
||||
"description": _("SMS Gateway URL, Message & Receiver Parameter"),
|
||||
"hide_count": True
|
||||
}
|
||||
]
|
||||
}]
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from six.moves import range
|
||||
from six import iteritems
|
||||
import frappe
|
||||
|
||||
|
|
@ -53,11 +52,11 @@ def get_reference_addresses_and_contact(reference_doctype, reference_name):
|
|||
filters = { "name": reference_name }
|
||||
|
||||
reference_list = [d[0] for d in frappe.get_list(reference_doctype, filters=filters, fields=["name"], as_list=True)]
|
||||
|
||||
for d in reference_list:
|
||||
reference_details.setdefault(d, frappe._dict())
|
||||
|
||||
reference_details = get_reference_details(reference_doctype, reference_list, "Address", reference_details)
|
||||
reference_details = get_reference_details(reference_doctype, reference_list, "Contact", reference_details)
|
||||
reference_details = get_reference_details(reference_doctype, "Address", reference_list, reference_details)
|
||||
reference_details = get_reference_details(reference_doctype, "Contact", reference_list, reference_details)
|
||||
|
||||
for reference_name, details in iteritems(reference_details):
|
||||
addresses = details.get("address", [])
|
||||
|
|
@ -68,21 +67,14 @@ def get_reference_addresses_and_contact(reference_doctype, reference_name):
|
|||
result.extend(add_blank_columns_for("Address"))
|
||||
data.append(result)
|
||||
else:
|
||||
addresses = map(list, addresses)
|
||||
contacts = map(list, contacts)
|
||||
result = [reference_name]
|
||||
result.extend(list(addresses) or add_blank_columns_for("Address"))
|
||||
result.extend(list(contacts) or add_blank_columns_for("Contact"))
|
||||
data.append(result)
|
||||
|
||||
max_length = max(len(addresses), len(contacts))
|
||||
for idx in range(0, max_length):
|
||||
result = [reference_name]
|
||||
address = addresses[idx] if idx < len(addresses) else add_blank_columns_for("Address")
|
||||
contact = contacts[idx] if idx < len(contacts) else add_blank_columns_for("Contact")
|
||||
result.extend(address)
|
||||
result.extend(contact)
|
||||
|
||||
data.append(result)
|
||||
return data
|
||||
|
||||
def get_reference_details(reference_doctype, reference_list, doctype, reference_details):
|
||||
def get_reference_details(reference_doctype, doctype, reference_list, reference_details):
|
||||
filters = [
|
||||
["Dynamic Link", "link_doctype", "=", reference_doctype],
|
||||
["Dynamic Link", "link_name", "in", reference_list]
|
||||
|
|
@ -91,9 +83,7 @@ def get_reference_details(reference_doctype, reference_list, doctype, reference_
|
|||
|
||||
records = frappe.get_list(doctype, filters=filters, fields=fields, as_list=True)
|
||||
for d in records:
|
||||
details = reference_details.get(d[0]) or {}
|
||||
details.setdefault(frappe.scrub(doctype), []).append(d[1:])
|
||||
|
||||
reference_details[d[0]][frappe.scrub(doctype)] = d[1:]
|
||||
return reference_details
|
||||
|
||||
def add_blank_columns_for(doctype):
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import frappe.defaults
|
||||
import unittest
|
||||
|
||||
from frappe.contacts.report.addresses_and_contacts.addresses_and_contacts import get_data
|
||||
|
||||
def get_custom_linked_doctype():
|
||||
if bool(frappe.get_all("DocType", filters={'name':'Test Custom Doctype'})):
|
||||
return
|
||||
|
||||
doc = frappe.get_doc({
|
||||
"doctype": "DocType",
|
||||
"module": "Core",
|
||||
"custom": 1,
|
||||
"fields": [{
|
||||
"label": "Test Field",
|
||||
"fieldname": "test_field",
|
||||
"fieldtype": "Data"
|
||||
},
|
||||
{
|
||||
"label": "Contact HTML",
|
||||
"fieldname": "contact_html",
|
||||
"fieldtype": "HTML"
|
||||
},
|
||||
{
|
||||
"label": "Address HTML",
|
||||
"fieldname": "address_html",
|
||||
"fieldtype": "HTML"
|
||||
}],
|
||||
"permissions": [{
|
||||
"role": "System Manager",
|
||||
"read": 1
|
||||
}],
|
||||
"name": "Test Custom Doctype",
|
||||
})
|
||||
doc.insert()
|
||||
|
||||
def get_custom_doc_for_address_and_contacts():
|
||||
get_custom_linked_doctype()
|
||||
linked_doc = frappe.get_doc({
|
||||
"doctype": "Test Custom Doctype",
|
||||
"test_field": "Hello",
|
||||
}).insert()
|
||||
return linked_doc
|
||||
|
||||
def create_linked_address(link_list):
|
||||
if frappe.flags.test_address_created:
|
||||
return
|
||||
|
||||
address = frappe.get_doc({
|
||||
"doctype": "Address",
|
||||
"address_title": "_Test Address",
|
||||
"address_type": "Billing",
|
||||
"address_line1": "test address line 1",
|
||||
"address_line2": "test address line 2",
|
||||
"city": "Milan",
|
||||
"country": "Italy"
|
||||
})
|
||||
|
||||
for name in link_list:
|
||||
address.append("links",{
|
||||
'link_doctype': 'Test Custom Doctype',
|
||||
'link_name': name
|
||||
})
|
||||
|
||||
address.insert()
|
||||
frappe.flags.test_address_created = True
|
||||
|
||||
def create_linked_contact(link_list):
|
||||
if frappe.flags.test_contact_created:
|
||||
return
|
||||
|
||||
contact = frappe.get_doc({
|
||||
"doctype": "Contact",
|
||||
"salutation": "Mr",
|
||||
"email_id": "test_contact@example.com",
|
||||
"first_name": "_Test First Name",
|
||||
"last_name": "_Test Last Name",
|
||||
"is_primary_contact": 1,
|
||||
"phone": "+91 0000000000",
|
||||
"status": "Open"
|
||||
})
|
||||
|
||||
for name in link_list:
|
||||
contact.append("links",{
|
||||
'link_doctype': 'Test Custom Doctype',
|
||||
'link_name': name
|
||||
})
|
||||
|
||||
contact.insert()
|
||||
frappe.flags.test_contact_created = True
|
||||
|
||||
|
||||
class TestAddressesAndContacts(unittest.TestCase):
|
||||
def test_get_data(self):
|
||||
linked_docs = [get_custom_doc_for_address_and_contacts(), get_custom_doc_for_address_and_contacts(), get_custom_doc_for_address_and_contacts()]
|
||||
links_list = [item.name for item in linked_docs]
|
||||
create_linked_contact(links_list)
|
||||
create_linked_address(links_list)
|
||||
report_data = get_data({"reference_doctype": "Test Custom Doctype"})
|
||||
for link in links_list:
|
||||
test_item = [link, 'test address line 1', 'test address line 2', 'Milan', None, None, 'Italy', 0, '_Test First Name', '_Test Last Name', '+91 0000000000', None, 'test_contact@example.com', 1]
|
||||
self.assertIn(test_item, report_data)
|
||||
|
||||
def tearDown(self):
|
||||
frappe.db.rollback()
|
||||
|
|
@ -75,8 +75,8 @@ def get_feed_match_conditions(user=None, force=True):
|
|||
if user_permissions:
|
||||
can_read_docs = []
|
||||
for doctype, obj in user_permissions.items():
|
||||
for n in obj.get("docs", []):
|
||||
can_read_docs.append('{}|{}'.format(doctype, frappe.db.escape(n)))
|
||||
for n in obj:
|
||||
can_read_docs.append('{}|{}'.format(doctype, frappe.db.escape(n.get('doc', ''))))
|
||||
|
||||
if can_read_docs:
|
||||
conditions.append("concat_ws('|', `tabCommunication`.reference_doctype, `tabCommunication`.reference_name) in ({})".format(
|
||||
|
|
|
|||
|
|
@ -96,15 +96,17 @@ def notify_mentions(doc):
|
|||
|
||||
recipients = [frappe.db.get_value("User", {"enabled": 1, "name": name, "user_type": "System User", "allowed_in_mentions": 1}, "email")
|
||||
for name in mentions]
|
||||
link = get_link_to_form(doc.reference_doctype, doc.reference_name, label=parent_doc_label)
|
||||
|
||||
frappe.sendmail(
|
||||
recipients=recipients,
|
||||
sender=frappe.session.user,
|
||||
subject=subject,
|
||||
template="mentioned_in_comment",
|
||||
args={
|
||||
"sender_fullname": sender_fullname,
|
||||
"body_content": _("{0} mentioned you in a comment in {1}").format(sender_fullname, link),
|
||||
"comment": doc,
|
||||
"link": get_link_to_form(doc.reference_doctype, doc.reference_name, label=parent_doc_label)
|
||||
"link": link
|
||||
},
|
||||
header=[_('New Mention'), 'orange']
|
||||
)
|
||||
|
|
|
|||
|
|
@ -34,8 +34,10 @@ def import_data(data_import):
|
|||
frappe.db.set_value("Data Import", data_import, "import_status", "In Progress", update_modified=False)
|
||||
frappe.publish_realtime("data_import_progress", {"progress": "0",
|
||||
"data_import": data_import, "reload": True}, user=frappe.session.user)
|
||||
|
||||
from frappe.core.page.background_jobs.background_jobs import get_info
|
||||
enqueued_jobs = [d.get("job_name") for d in get_info()]
|
||||
|
||||
if data_import not in enqueued_jobs:
|
||||
enqueue(upload, queue='default', timeout=6000, event='data_import', job_name=data_import,
|
||||
data_import_doc=data_import, from_data_import="Yes", user=frappe.session.user)
|
||||
|
|
|
|||
|
|
@ -434,15 +434,28 @@ def upload(rows = None, submit_after_import=None, ignore_encoding_errors=False,
|
|||
|
||||
except Exception as e:
|
||||
error_flag = True
|
||||
err_msg = frappe.local.message_log and "\n".join([json.loads(msg).get('message') for msg in frappe.local.message_log]) or cstr(e)
|
||||
|
||||
# build error message
|
||||
if frappe.local.message_log:
|
||||
err_msg = "\n".join(['<p class="border-bottom small">{}</p>'.format(json.loads(msg).get('message')) for msg in frappe.local.message_log])
|
||||
else:
|
||||
err_msg = '<p class="border-bottom small">{}</p>'.format(cstr(e))
|
||||
|
||||
error_trace = frappe.get_traceback()
|
||||
if error_trace:
|
||||
error_log_doc = frappe.log_error(error_trace)
|
||||
error_link = get_url_to_form("Error Log", error_log_doc.name)
|
||||
else:
|
||||
error_link = None
|
||||
log(**{"row": row_idx + 1, "title":'Error for row %s' % (len(row)>1 and frappe.safe_decode(row[1]) or ""), "message": err_msg,
|
||||
"indicator": "red", "link":error_link})
|
||||
|
||||
log(**{
|
||||
"row": row_idx + 1,
|
||||
"title": 'Error for row %s' % (len(row)>1 and frappe.safe_decode(row[1]) or ""),
|
||||
"message": err_msg,
|
||||
"indicator": "red",
|
||||
"link":error_link
|
||||
})
|
||||
|
||||
# data with error to create a new file
|
||||
# include the errored data in the last row as last_error_row_idx will not be updated for the last row
|
||||
if skip_errors:
|
||||
|
|
|
|||
|
|
@ -6,19 +6,19 @@
|
|||
<th style="width:40%"> {{ __("Row Status") }} </th>
|
||||
<th style="width:50%"> {{ __("Message") }} </th>
|
||||
</tr>
|
||||
|
||||
|
||||
{% for row in data %}
|
||||
{% if (!show_only_errors) || (show_only_errors && row.indicator == "red") %}
|
||||
<tr>
|
||||
<td>
|
||||
<span>{{ row.row }} </span>
|
||||
<span>{{ row.row }} </span>
|
||||
</td>
|
||||
<td>
|
||||
<span class="indicator {{ row.indicator }}"> {{ row.title }} </span>
|
||||
</td>
|
||||
<td>
|
||||
{% if (import_status != "Failed" || (row.indicator == "red")) { %}
|
||||
<span> {{ row.message }} </span>
|
||||
<div>{{ row.message }}</div>
|
||||
{% if row.link %}
|
||||
<span style="width: 10%; float:right;">
|
||||
<a class="btn-open no-decoration" title="Open Link" href="{{ row.link }}">
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,7 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import frappe.share
|
||||
import unittest
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import frappe
|
|||
from frappe import _
|
||||
|
||||
from frappe.utils import now, cint
|
||||
from frappe.model import no_value_fields, default_fields, data_fieldtypes
|
||||
from frappe.model import no_value_fields, default_fields, data_fieldtypes, table_fields
|
||||
from frappe.model.document import Document
|
||||
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
|
||||
from frappe.desk.notifications import delete_notification_count_for
|
||||
|
|
@ -82,7 +82,7 @@ class DocType(Document):
|
|||
if not [d.fieldname for d in self.fields if d.in_list_view]:
|
||||
cnt = 0
|
||||
for d in self.fields:
|
||||
if d.reqd and not d.hidden and not d.fieldtype == "Table":
|
||||
if d.reqd and not d.hidden and not d.fieldtype in table_fields:
|
||||
d.in_list_view = 1
|
||||
cnt += 1
|
||||
if cnt == 4: break
|
||||
|
|
@ -171,7 +171,8 @@ class DocType(Document):
|
|||
"""Change the timestamp of parent DocType if the current one is a child to clear caches."""
|
||||
if frappe.flags.in_import:
|
||||
return
|
||||
parent_list = frappe.db.get_all('DocField', 'parent', dict(fieldtype='Table', options=self.name))
|
||||
parent_list = frappe.db.get_all('DocField', 'parent',
|
||||
dict(fieldtype=['in', frappe.model.table_fields], options=self.name))
|
||||
for p in parent_list:
|
||||
frappe.db.sql('UPDATE `tabDocType` SET modified=%s WHERE `name`=%s', (now(), p.parent))
|
||||
|
||||
|
|
@ -511,11 +512,11 @@ def validate_fields(meta):
|
|||
validate_column_length(fieldname)
|
||||
|
||||
def check_illegal_mandatory(d):
|
||||
if (d.fieldtype in no_value_fields) and d.fieldtype!="Table" and d.reqd:
|
||||
if (d.fieldtype in no_value_fields) and d.fieldtype not in table_fields and d.reqd:
|
||||
frappe.throw(_("Field {0} of type {1} cannot be mandatory").format(d.label, d.fieldtype))
|
||||
|
||||
def check_link_table_options(d):
|
||||
if d.fieldtype in ("Link", "Table"):
|
||||
if d.fieldtype in ("Link",) + table_fields:
|
||||
if not d.options:
|
||||
frappe.throw(_("Options required for Link or Table type field {0} in row {1}").format(d.label, d.idx))
|
||||
if d.options=="[Select]" or d.options==d.parent:
|
||||
|
|
@ -692,6 +693,19 @@ def validate_fields(meta):
|
|||
re.match("""[\w\.:_]+\s*={1}\s*[\w\.@'"]+""", depends_on):
|
||||
frappe.throw(_("Invalid {0} condition").format(frappe.unscrub(field)), frappe.ValidationError)
|
||||
|
||||
def check_table_multiselect_option(docfield):
|
||||
'''check if the doctype provided in Option has atleast 1 Link field'''
|
||||
if not docfield.fieldtype == 'Table MultiSelect': return
|
||||
|
||||
doctype = docfield.options
|
||||
meta = frappe.get_meta(doctype)
|
||||
link_field = [df for df in meta.fields if df.fieldtype == 'Link']
|
||||
|
||||
if not link_field:
|
||||
frappe.throw(_('DocType <b>{0}</b> provided for the field <b>{1}</b> must have atleast one Link field')
|
||||
.format(doctype, docfield.fieldname), frappe.ValidationError)
|
||||
|
||||
|
||||
fields = meta.get("fields")
|
||||
fieldname_list = [d.fieldname for d in fields]
|
||||
|
||||
|
|
@ -702,7 +716,7 @@ def validate_fields(meta):
|
|||
|
||||
for d in fields:
|
||||
if not d.permlevel: d.permlevel = 0
|
||||
if d.fieldtype != "Table": d.allow_bulk_edit = 0
|
||||
if d.fieldtype not in table_fields: d.allow_bulk_edit = 0
|
||||
if d.fieldtype == "Barcode": d.ignore_xss_filter = 1
|
||||
if not d.fieldname:
|
||||
d.fieldname = d.fieldname.lower()
|
||||
|
|
@ -719,6 +733,7 @@ def validate_fields(meta):
|
|||
check_illegal_default(d)
|
||||
check_unique_and_text(d)
|
||||
check_illegal_depends_on_conditions(d)
|
||||
check_table_multiselect_option(d)
|
||||
|
||||
check_fold(fields)
|
||||
check_search_fields(meta, fields)
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ class File(NestedSet):
|
|||
self.validate_file_name()
|
||||
self.validate_folder()
|
||||
|
||||
if not self.flags.ignore_file_validate:
|
||||
if not self.file_url and not self.flags.ignore_file_validate:
|
||||
if not self.is_folder:
|
||||
self.validate_file()
|
||||
self.generate_content_hash()
|
||||
|
|
@ -386,9 +386,11 @@ class File(NestedSet):
|
|||
|
||||
elif file_path.startswith("/files/"):
|
||||
file_path = get_files_path(*file_path.split("/files/", 1)[1].split("/"))
|
||||
|
||||
elif file_path.startswith("http"):
|
||||
pass
|
||||
else:
|
||||
|
||||
elif not self.file_url:
|
||||
frappe.throw(_("There is some problem with the file url: {0}").format(file_path))
|
||||
|
||||
return file_path
|
||||
|
|
@ -817,12 +819,9 @@ def download_file(file_url):
|
|||
"""
|
||||
file_doc = frappe.get_doc("File", {"file_url": file_url})
|
||||
file_doc.check_permission("read")
|
||||
path = os.path.join(get_files_path(), os.path.basename(file_url))
|
||||
|
||||
with open(path, "rb") as fileobj:
|
||||
filedata = fileobj.read()
|
||||
frappe.local.response.filename = os.path.basename(file_url)
|
||||
frappe.local.response.filecontent = filedata
|
||||
frappe.local.response.filecontent = file_doc.get_content()
|
||||
frappe.local.response.type = "download"
|
||||
|
||||
def extract_images_from_doc(doc, fieldname):
|
||||
|
|
|
|||
|
|
@ -65,8 +65,8 @@ def create_json_gz_file(data, dt, dn):
|
|||
"file_name": json_filename,
|
||||
"attached_to_doctype": dt,
|
||||
"attached_to_name": dn,
|
||||
"content": compressed_content,
|
||||
"decode": True})
|
||||
"content": compressed_content
|
||||
})
|
||||
_file.save()
|
||||
|
||||
@frappe.whitelist()
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
|
|
@ -42,7 +43,7 @@
|
|||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
|
|
@ -562,7 +563,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "prepared_report",
|
||||
"fieldname": "disable_prepared_report",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
|
|
@ -571,15 +572,47 @@
|
|||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Disable Prepared Report",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "prepared_report",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Prepared Report",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Yes\nNo",
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
|
|
@ -600,7 +633,7 @@
|
|||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-06-27 14:48:49.989952",
|
||||
"modified": "2019-01-25 12:04:50.833264",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Report",
|
||||
|
|
|
|||
|
|
@ -118,14 +118,16 @@ class Report(Document):
|
|||
if fieldtype and '/' in fieldtype:
|
||||
fieldtype, options = fieldtype.split('/')
|
||||
|
||||
columns.append(frappe._dict(label=parts[0], fieldtype=fieldtype, fieldname=parts[0]))
|
||||
columns.append(frappe._dict(label=parts[0], fieldtype=fieldtype, fieldname=parts[0], options=options))
|
||||
|
||||
out += data.get('result')
|
||||
else:
|
||||
# standard report
|
||||
params = json.loads(self.json)
|
||||
|
||||
if params.get('columns'):
|
||||
if params.get('fields'):
|
||||
columns = params.get('fields')
|
||||
elif params.get('columns'):
|
||||
columns = params.get('columns')
|
||||
elif params.get('fields'):
|
||||
columns = params.get('fields')
|
||||
|
|
@ -165,6 +167,7 @@ class Report(Document):
|
|||
user=user)
|
||||
|
||||
_columns = []
|
||||
|
||||
for column in columns:
|
||||
meta = frappe.get_meta(column[1])
|
||||
field = [meta.get_field(column[0]) or frappe._dict(label=meta.get_label(column[0]), fieldname=column[0])]
|
||||
|
|
@ -192,3 +195,8 @@ class Report(Document):
|
|||
@Document.whitelist
|
||||
def toggle_disable(self, disable):
|
||||
self.db_set("disabled", cint(disable))
|
||||
|
||||
@frappe.whitelist()
|
||||
def is_prepared_report_disabled(report):
|
||||
return frappe.db.get_value('Report',
|
||||
report, 'disable_prepared_report') or 0
|
||||
|
|
|
|||
|
|
@ -9,10 +9,14 @@ frappe.ui.form.on('Role Permission for Page and Report', {
|
|||
refresh: function(frm) {
|
||||
frm.disable_save();
|
||||
frm.role_area.hide();
|
||||
frm.add_custom_button(__("Reset to defaults"),
|
||||
function(){ frm.trigger("reset_roles") });
|
||||
frm.add_custom_button(__("Update"),
|
||||
function(){ frm.trigger("update_roles") }).addClass('btn-primary');
|
||||
|
||||
frm.add_custom_button(__("Reset to defaults"), function() {
|
||||
frm.trigger("reset_roles");
|
||||
});
|
||||
|
||||
frm.add_custom_button(__("Update"), function() {
|
||||
frm.trigger("update_report_page_data");
|
||||
}).addClass('btn-primary');
|
||||
},
|
||||
|
||||
onload: function(frm) {
|
||||
|
|
@ -45,22 +49,22 @@ frappe.ui.form.on('Role Permission for Page and Report', {
|
|||
|
||||
page: function(frm) {
|
||||
if(frm.doc.page) {
|
||||
frm.trigger("get_roles")
|
||||
frm.trigger("set_report_page_data");
|
||||
}
|
||||
},
|
||||
|
||||
report: function(frm){
|
||||
if(frm.doc.report) {
|
||||
frm.trigger("get_roles")
|
||||
frm.trigger("set_report_page_data");
|
||||
}
|
||||
},
|
||||
|
||||
get_roles: function(frm) {
|
||||
set_report_page_data: function(frm) {
|
||||
frm.toggle_display('roles_html', true)
|
||||
frm.role_area.show();
|
||||
|
||||
return frm.call({
|
||||
method:"get_custom_roles",
|
||||
method:"set_report_page_data",
|
||||
doc: frm.doc,
|
||||
callback: function(r) {
|
||||
refresh_field('roles')
|
||||
|
|
@ -69,14 +73,14 @@ frappe.ui.form.on('Role Permission for Page and Report', {
|
|||
})
|
||||
},
|
||||
|
||||
update_roles: function(frm) {
|
||||
update_report_page_data: function(frm) {
|
||||
frm.trigger("validate_mandatory_fields")
|
||||
if(frm.roles_editor) {
|
||||
frm.roles_editor.set_roles_in_table()
|
||||
}
|
||||
|
||||
return frm.call({
|
||||
method:"set_custom_roles",
|
||||
method:"update_report_page_data",
|
||||
doc: frm.doc,
|
||||
callback: function(r) {
|
||||
refresh_field('roles')
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"allow_copy": 1,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
|
|
@ -13,6 +14,8 @@
|
|||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
|
|
@ -24,7 +27,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": "Set Role For",
|
||||
"length": 0,
|
||||
|
|
@ -40,9 +43,12 @@
|
|||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
|
|
@ -71,9 +77,12 @@
|
|||
"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,
|
||||
|
|
@ -102,9 +111,77 @@
|
|||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_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
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "report",
|
||||
"fetch_from": "",
|
||||
"fieldname": "disable_prepared_report",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Disable Prepared Report",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"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,
|
||||
|
|
@ -131,9 +208,12 @@
|
|||
"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,
|
||||
|
|
@ -161,9 +241,12 @@
|
|||
"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,
|
||||
|
|
@ -191,6 +274,7 @@
|
|||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
|
|
@ -204,7 +288,7 @@
|
|||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-03-11 02:35:32.369043",
|
||||
"modified": "2019-01-25 12:08:57.250719",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Role Permission for Page and Report",
|
||||
|
|
@ -213,7 +297,6 @@
|
|||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
|
|
@ -239,5 +322,6 @@
|
|||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 0,
|
||||
"track_seen": 0
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
||||
|
|
@ -4,10 +4,15 @@
|
|||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.core.doctype.report.report import is_prepared_report_disabled
|
||||
from frappe.model.document import Document
|
||||
|
||||
class RolePermissionforPageandReport(Document):
|
||||
def get_custom_roles(self):
|
||||
def set_report_page_data(self):
|
||||
self.set_custom_roles()
|
||||
self.check_prepared_report_disabled()
|
||||
|
||||
def set_custom_roles(self):
|
||||
args = self.get_args()
|
||||
self.set('roles', [])
|
||||
|
||||
|
|
@ -19,7 +24,11 @@ class RolePermissionforPageandReport(Document):
|
|||
roles = self.get_standard_roles()
|
||||
|
||||
self.set('roles', roles)
|
||||
|
||||
|
||||
def check_prepared_report_disabled(self):
|
||||
if self.report:
|
||||
self.disable_prepared_report = is_prepared_report_disabled(self.report)
|
||||
|
||||
def get_standard_roles(self):
|
||||
doctype = self.set_role_for
|
||||
docname = self.page if self.set_role_for == 'Page' else self.report
|
||||
|
|
@ -29,9 +38,14 @@ class RolePermissionforPageandReport(Document):
|
|||
def reset_roles(self):
|
||||
roles = self.get_standard_roles()
|
||||
self.set('roles', roles)
|
||||
self.set_custom_roles()
|
||||
self.update_custom_roles()
|
||||
self.update_disable_prepared_report()
|
||||
|
||||
def set_custom_roles(self):
|
||||
def update_report_page_data(self):
|
||||
self.update_custom_roles()
|
||||
self.update_disable_prepared_report()
|
||||
|
||||
def update_custom_roles(self):
|
||||
args = self.get_args()
|
||||
name = frappe.db.get_value('Custom Role', args, "name")
|
||||
|
||||
|
|
@ -50,6 +64,10 @@ class RolePermissionforPageandReport(Document):
|
|||
else:
|
||||
frappe.get_doc(args).insert()
|
||||
|
||||
def update_disable_prepared_report(self):
|
||||
if self.report:
|
||||
frappe.db.set_value('Report', self.report, 'disable_prepared_report', self.disable_prepared_report)
|
||||
|
||||
def get_args(self, row=None):
|
||||
name = self.page if self.set_role_for == 'Page' else self.report
|
||||
check_for_field = self.set_role_for.replace(" ","_").lower()
|
||||
|
|
|
|||
|
|
@ -1624,7 +1624,7 @@
|
|||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-10-01 17:35:13.043855",
|
||||
"modified": "2019-01-30 11:02:41.011412",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "System Settings",
|
||||
|
|
|
|||
16
frappe/core/doctype/transaction_log/readme.md
Normal file
16
frappe/core/doctype/transaction_log/readme.md
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# Transaction Log Changelog
|
||||
|
||||
## v1.0.0
|
||||
Initial version
|
||||
|
||||
The line hash summarizes:
|
||||
- The index of the row
|
||||
- The timestamp
|
||||
- The document raw data
|
||||
|
||||
The chain hash summarizes:
|
||||
- The previous line hash
|
||||
- The current line hash
|
||||
|
||||
## v1.0.1
|
||||
Modification of the timestamp fieldtype from "Time" to "Datetime"
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
|
|
@ -178,7 +179,7 @@
|
|||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "timestamp",
|
||||
"fieldtype": "Time",
|
||||
"fieldtype": "Datetime",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
|
|
@ -436,7 +437,7 @@
|
|||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-09-21 08:49:07.915376",
|
||||
"modified": "2018-12-27 15:22:34.533766",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Transaction Log",
|
||||
|
|
|
|||
|
|
@ -6,14 +6,14 @@ from __future__ import unicode_literals
|
|||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import now, cint
|
||||
from frappe.utils import cint, now_datetime
|
||||
import hashlib
|
||||
|
||||
class TransactionLog(Document):
|
||||
def before_insert(self):
|
||||
index = get_current_index()
|
||||
self.row_index = index
|
||||
self.timestamp = now()
|
||||
self.timestamp = now_datetime()
|
||||
if index != 1:
|
||||
prev_hash = frappe.db.sql(
|
||||
"SELECT `chaining_hash` FROM `tabTransaction Log` WHERE `row_index` = '{0}'".format(index - 1))
|
||||
|
|
@ -25,7 +25,7 @@ class TransactionLog(Document):
|
|||
self.previous_hash = self.hash_line()
|
||||
self.transaction_hash = self.hash_line()
|
||||
self.chaining_hash = self.hash_chain()
|
||||
self.checksum_version = "v1.0.0"
|
||||
self.checksum_version = "v1.0.1"
|
||||
|
||||
def hash_line(self):
|
||||
sha = hashlib.sha256()
|
||||
|
|
|
|||
|
|
@ -891,10 +891,9 @@ def get_active_website_users():
|
|||
def get_permission_query_conditions(user):
|
||||
if user=="Administrator":
|
||||
return ""
|
||||
|
||||
else:
|
||||
return """(`tabUser`.name not in ({standard_users}))""".format(
|
||||
standard_users='"' + '", "'.join(STANDARD_USERS) + '"')
|
||||
standard_users = ", ".join(frappe.db.escape(user) for user in STANDARD_USERS))
|
||||
|
||||
def has_permission(doc, user):
|
||||
if (user != "Administrator") and (doc.name in STANDARD_USERS):
|
||||
|
|
@ -932,7 +931,7 @@ def handle_password_test_fail(result):
|
|||
suggestions = result['feedback']['suggestions'][0] if result['feedback']['suggestions'] else ''
|
||||
warning = result['feedback']['warning'] if 'warning' in result['feedback'] else ''
|
||||
suggestions += "<br>" + _("Hint: Include symbols, numbers and capital letters in the password") + '<br>'
|
||||
frappe.throw(_('Invalid Password: ' + ' '.join([warning, suggestions])))
|
||||
frappe.throw(' '.join([_('Invalid Password:'), warning, suggestions]))
|
||||
|
||||
def update_gravatar(name):
|
||||
gravatar = has_gravatar(name)
|
||||
|
|
|
|||
|
|
@ -56,10 +56,10 @@ def get_user_permissions(user=None):
|
|||
if not out.get(perm.allow):
|
||||
out[perm.allow] = []
|
||||
|
||||
out[perm.allow].append({
|
||||
out[perm.allow].append(frappe._dict({
|
||||
'doc': doc_name,
|
||||
'applicable_for': perm.get('applicable_for')
|
||||
})
|
||||
}))
|
||||
|
||||
try:
|
||||
for perm in frappe.get_all('User Permission',
|
||||
|
|
@ -74,6 +74,7 @@ def get_user_permissions(user=None):
|
|||
for doc in decendants:
|
||||
add_doc_to_perm(perm, doc)
|
||||
|
||||
out = frappe._dict(out)
|
||||
frappe.cache().hset("user_permissions", user, out)
|
||||
except frappe.db.SQLError:
|
||||
if frappe.db.is_table_missing():
|
||||
|
|
@ -94,6 +95,7 @@ def get_applicable_for_doctype_list(doctype, txt, searchfield, start, page_len,
|
|||
linked_doctypes = get_linked_doctypes(doctype, True).keys()
|
||||
linked_doctypes = list(linked_doctypes)
|
||||
linked_doctypes += [doctype]
|
||||
|
||||
if txt:
|
||||
linked_doctypes = [d for d in linked_doctypes if txt in d.lower()]
|
||||
|
||||
|
|
@ -108,3 +110,13 @@ def get_applicable_for_doctype_list(doctype, txt, searchfield, start, page_len,
|
|||
def get_permitted_documents(doctype):
|
||||
return [d.get('doc') for d in get_user_permissions().get(doctype, []) \
|
||||
if d.get('doc')]
|
||||
|
||||
@frappe.whitelist()
|
||||
def clear_user_permissions(user, for_doctype):
|
||||
frappe.only_for('System Manager')
|
||||
|
||||
total = frappe.db.count('User Permission', filters = dict(user=user, allow=for_doctype))
|
||||
if total:
|
||||
frappe.db.sql('DELETE FROM `tabUser Permission` WHERE `user`=%s AND `allow`=%s', (user, for_doctype))
|
||||
frappe.clear_cache()
|
||||
return total
|
||||
|
|
|
|||
52
frappe/core/doctype/user_permission/user_permission_list.js
Normal file
52
frappe/core/doctype/user_permission/user_permission_list.js
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
frappe.listview_settings['User Permission'] = {
|
||||
onload: function(list_view) {
|
||||
list_view.page.add_menu_item(__("Clear User Permissions"), () => {
|
||||
const dialog = new frappe.ui.Dialog({
|
||||
title: __('Clear User Permissions'),
|
||||
fields: [
|
||||
{
|
||||
'fieldname': 'user',
|
||||
'label': __('For User'),
|
||||
'fieldtype': 'Link',
|
||||
'options': 'User',
|
||||
'reqd': 1
|
||||
},
|
||||
{
|
||||
'fieldname': 'for_doctype',
|
||||
'label': __('For Document Type'),
|
||||
'fieldtype': 'Link',
|
||||
'options': 'DocType',
|
||||
'reqd': 1
|
||||
},
|
||||
],
|
||||
primary_action: (data) => {
|
||||
// mandatory not filled
|
||||
if (!data) return;
|
||||
|
||||
frappe.confirm(__('Are you sure?'), () => {
|
||||
frappe
|
||||
.xcall('frappe.core.doctype.user_permission.user_permission.clear_user_permissions', data)
|
||||
.then(data => {
|
||||
dialog.hide();
|
||||
let message = '';
|
||||
if (data === 0) {
|
||||
message = __('No records deleted');
|
||||
} else {
|
||||
message = __('{0} records deleted', [data]);
|
||||
}
|
||||
frappe.show_alert({
|
||||
message,
|
||||
indicator: 'green'
|
||||
});
|
||||
list_view.refresh();
|
||||
});
|
||||
});
|
||||
|
||||
},
|
||||
primary_action_label: __('Clear')
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -7,7 +7,7 @@ from __future__ import unicode_literals
|
|||
import frappe, json
|
||||
|
||||
from frappe.model.document import Document
|
||||
from frappe.model import no_value_fields
|
||||
from frappe.model import no_value_fields, table_fields
|
||||
|
||||
class Version(Document):
|
||||
def set_diff(self, old, new):
|
||||
|
|
@ -42,12 +42,12 @@ def get_diff(old, new, for_child=False):
|
|||
}'''
|
||||
out = frappe._dict(changed = [], added = [], removed = [], row_changed = [])
|
||||
for df in new.meta.fields:
|
||||
if df.fieldtype in no_value_fields and df.fieldtype != 'Table':
|
||||
if df.fieldtype in no_value_fields and df.fieldtype not in table_fields:
|
||||
continue
|
||||
|
||||
old_value, new_value = old.get(df.fieldname), new.get(df.fieldname)
|
||||
|
||||
if df.fieldtype=='Table':
|
||||
if df.fieldtype in table_fields:
|
||||
# make maps
|
||||
old_row_by_name, new_row_by_name = {}, {}
|
||||
for d in old_value:
|
||||
|
|
|
|||
|
|
@ -2,15 +2,15 @@
|
|||
// rename this file from _test_[name] to test_[name] to activate
|
||||
// and remove above this line
|
||||
|
||||
QUnit.test("test: View log", function (assert) {
|
||||
QUnit.test("test: View Log", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
// number of asserts
|
||||
assert.expect(1);
|
||||
|
||||
frappe.run_serially([
|
||||
// insert a new View log
|
||||
() => frappe.tests.make('View log', [
|
||||
// insert a new View Log
|
||||
() => frappe.tests.make('View Log', [
|
||||
// values to be set
|
||||
{key: 'value'}
|
||||
]),
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from __future__ import unicode_literals
|
|||
import frappe
|
||||
import unittest
|
||||
|
||||
class TestViewlog(unittest.TestCase):
|
||||
class TestViewLog(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
frappe.set_user('Administrator')
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ class TestViewlog(unittest.TestCase):
|
|||
# load the form
|
||||
getdoc('Event', ev.name)
|
||||
a = frappe.get_value(
|
||||
doctype="View log",
|
||||
doctype="View Log",
|
||||
filters={
|
||||
"reference_doctype": "Event",
|
||||
"reference_name": ev.name
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2018, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('View log', {
|
||||
frappe.ui.form.on('View Log', {
|
||||
refresh: function(frm) {
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,160 +1,161 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2018-05-27 02:20:11.193944",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2018-05-27 02:20:11.193944",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "viewed_by",
|
||||
"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": "Viewed By",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 1,
|
||||
"translatable": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "viewed_by",
|
||||
"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": "Viewed By",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 1,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "reference_doctype",
|
||||
"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": "Reference doctype",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 1,
|
||||
"translatable": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "reference_doctype",
|
||||
"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": "Reference doctype",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 1,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "reference_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Reference name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "reference_doctype",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 1,
|
||||
"translatable": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "reference_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Reference name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "reference_doctype",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 1,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-05-30 11:21:35.258019",
|
||||
"modified_by": "shridhar.p@zerodha.com",
|
||||
"module": "Core",
|
||||
"name": "View log",
|
||||
"name_case": "",
|
||||
"owner": "shridhar.p@zerodha.com",
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-01-03 13:04:31.389182",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "View Log",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
||||
|
|
@ -6,5 +6,5 @@ from __future__ import unicode_literals
|
|||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class Viewlog(Document):
|
||||
class ViewLog(Document):
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
// Copyright (c) 2016, Frappe Technologies and contributors
|
||||
// Copyright (c) 2019, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.query_reports["Transaction Log Report"] = {
|
||||
|
||||
onload: function(query_report) {
|
||||
query_report.add_make_chart_button = function() {
|
||||
//
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2018-06-29 15:46:46.884862",
|
||||
"modified": "2018-12-27 18:10:29.785415",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Transaction Log Report",
|
||||
|
|
@ -18,6 +18,9 @@
|
|||
"roles": [
|
||||
{
|
||||
"role": "Administrator"
|
||||
},
|
||||
{
|
||||
"role": "System Manager"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,10 +1,11 @@
|
|||
# Copyright (c) 2013, Frappe Technologies and contributors
|
||||
# Copyright (c) 2019, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import hashlib
|
||||
from frappe import _
|
||||
from frappe.utils import format_datetime
|
||||
|
||||
def execute(filters=None):
|
||||
columns, data = get_columns(filters), get_data(filters)
|
||||
|
|
@ -24,9 +25,9 @@ def get_data(filters=None):
|
|||
else:
|
||||
integrity = check_data_integrity(l.chaining_hash, l.transaction_hash, l.previous_hash, previous_hash[0][0])
|
||||
|
||||
result.append([str(integrity), l.reference_doctype, l.document_name, l.owner, l.modified_by, l.timestamp])
|
||||
result.append([_(str(integrity)), _(l.reference_doctype), l.document_name, l.owner, l.modified_by, format_datetime(l.timestamp, "YYYYMMDDHHmmss")])
|
||||
else:
|
||||
result.append([_("First Transaction"), l.reference_doctype, l.document_name, l.owner, l.modified_by, l.timestamp])
|
||||
result.append([_("First Transaction"), _(l.reference_doctype), l.document_name, l.owner, l.modified_by, format_datetime(l.timestamp, "YYYYMMDDHHmmss")])
|
||||
|
||||
return result
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
|
||||
def get_parent_doc(doc):
|
||||
"""Returns document of `reference_doctype`, `reference_doctype`"""
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
"is_standard": 1,
|
||||
"login_required": 1,
|
||||
"max_attachment_size": 0,
|
||||
"modified": "2018-11-01 19:35:43.552429",
|
||||
"modified": "2019-01-28 12:45:17.158069",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "edit-profile",
|
||||
|
|
@ -129,19 +129,6 @@
|
|||
"read_only": 0,
|
||||
"reqd": 0,
|
||||
"show_in_filter": 0
|
||||
},
|
||||
{
|
||||
"allow_read_on_all_link_options": 0,
|
||||
"fieldname": "roles",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"label": "Roles Assigned",
|
||||
"max_length": 0,
|
||||
"max_value": 0,
|
||||
"options": "Has Role",
|
||||
"read_only": 0,
|
||||
"reqd": 0,
|
||||
"show_in_filter": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -9,6 +9,9 @@ frappe.ui.form.on('Custom Field', {
|
|||
frm.set_query('dt', function(doc) {
|
||||
var filters = [
|
||||
['DocType', 'issingle', '=', 0],
|
||||
['DocType', 'custom', '=', 0],
|
||||
['DocType', 'name', 'not in', frappe.model.core_doctypes_list],
|
||||
['DocType', 'restrict_to_domain', 'in', frappe.boot.active_domains]
|
||||
];
|
||||
if(frappe.session.user!=="Administrator") {
|
||||
filters.push(['DocType', 'module', 'not in', ['Core', 'Custom']])
|
||||
|
|
@ -32,15 +35,21 @@ frappe.ui.form.on('Custom Field', {
|
|||
return frappe.call({
|
||||
method: 'frappe.custom.doctype.custom_field.custom_field.get_fields_label',
|
||||
args: { doctype: frm.doc.dt, fieldname: frm.doc.fieldname },
|
||||
callback: function(r, rt) {
|
||||
set_field_options('insert_after', r.message);
|
||||
var fieldnames = $.map(r.message, function(v) { return v.value; });
|
||||
callback: function(r) {
|
||||
if(r) {
|
||||
if(r._server_messages && r._server_messages.length) {
|
||||
frm.set_value("dt", "");
|
||||
} else {
|
||||
set_field_options('insert_after', r.message);
|
||||
var fieldnames = $.map(r.message, function(v) { return v.value; });
|
||||
|
||||
if(insert_after==null || !in_list(fieldnames, insert_after)) {
|
||||
insert_after = fieldnames[-1];
|
||||
if(insert_after==null || !in_list(fieldnames, insert_after)) {
|
||||
insert_after = fieldnames[-1];
|
||||
}
|
||||
|
||||
frm.set_value('insert_after', insert_after);
|
||||
}
|
||||
}
|
||||
|
||||
frm.set_value('insert_after', insert_after);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -234,7 +234,7 @@
|
|||
"no_copy": 0,
|
||||
"oldfieldname": "fieldtype",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nGeolocation\nHTML\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nText\nText Editor\nTime\nSignature",
|
||||
"options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nGeolocation\nHTML\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
|
|
@ -1302,7 +1302,7 @@
|
|||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-11-23 19:56:43.328280",
|
||||
"modified": "2018-12-19 18:34:46.031246",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Custom",
|
||||
"name": "Custom Field",
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ from frappe.utils import cstr
|
|||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.docfield import supports_translation
|
||||
from frappe.model import core_doctypes_list
|
||||
|
||||
class CustomField(Document):
|
||||
def autoname(self):
|
||||
|
|
@ -85,6 +86,14 @@ class CustomField(Document):
|
|||
|
||||
@frappe.whitelist()
|
||||
def get_fields_label(doctype=None):
|
||||
meta = frappe.get_meta(doctype)
|
||||
|
||||
if doctype in core_doctypes_list:
|
||||
return frappe.msgprint(_("Custom Fields cannot be added to core DocTypes."))
|
||||
|
||||
if meta.custom:
|
||||
return frappe.msgprint(_("Custom Fields can only be added to a standard DocType."))
|
||||
|
||||
return [{"value": df.fieldname or "", "label": _(df.label or "")}
|
||||
for df in frappe.get_meta(doctype).get("fields")]
|
||||
|
||||
|
|
|
|||
|
|
@ -13,9 +13,7 @@ frappe.ui.form.on("Customize Form", {
|
|||
filters: [
|
||||
['DocType', 'issingle', '=', 0],
|
||||
['DocType', 'custom', '=', 0],
|
||||
['DocType', 'name', 'not in', 'DocType, DocField, DocPerm, User, Role, Has Role, \
|
||||
Page, Has Role, Module Def, Print Format, Report, Customize Form, \
|
||||
Customize Form Field, Property Setter, Custom Field, Custom Script'],
|
||||
['DocType', 'name', 'not in', frappe.model.core_doctypes_list],
|
||||
['DocType', 'restrict_to_domain', 'in', frappe.boot.active_domains]
|
||||
]
|
||||
};
|
||||
|
|
@ -39,8 +37,14 @@ frappe.ui.form.on("Customize Form", {
|
|||
doc: frm.doc,
|
||||
freeze: true,
|
||||
callback: function(r) {
|
||||
frm.refresh();
|
||||
frm.trigger("setup_sortable");
|
||||
if(r) {
|
||||
if(r._server_messages && r._server_messages.length) {
|
||||
frm.set_value("doc_type", "");
|
||||
} else {
|
||||
frm.refresh();
|
||||
frm.trigger("setup_sortable");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import frappe.translate
|
|||
from frappe import _
|
||||
from frappe.utils import cint
|
||||
from frappe.model.document import Document
|
||||
from frappe.model import no_value_fields
|
||||
from frappe.model import no_value_fields, core_doctypes_list
|
||||
from frappe.core.doctype.doctype.doctype import validate_fields_for_doctype
|
||||
from frappe.model.docfield import supports_translation
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ docfield_properties = {
|
|||
|
||||
allowed_fieldtype_change = (('Currency', 'Float', 'Percent'), ('Small Text', 'Data'),
|
||||
('Text', 'Data'), ('Text', 'Text Editor', 'Code', 'Signature', 'HTML Editor'), ('Data', 'Select'),
|
||||
('Text', 'Small Text'), ('Text', 'Data', 'Barcode'), ('Code', 'Geolocation'))
|
||||
('Text', 'Small Text'), ('Text', 'Data', 'Barcode'), ('Code', 'Geolocation'), ('Table', 'Table MultiSelect'))
|
||||
|
||||
allowed_fieldtype_for_options_change = ('Read Only', 'HTML', 'Select', 'Data')
|
||||
|
||||
|
|
@ -85,6 +85,12 @@ class CustomizeForm(Document):
|
|||
|
||||
meta = frappe.get_meta(self.doc_type)
|
||||
|
||||
if self.doc_type in core_doctypes_list:
|
||||
return frappe.msgprint(_("Core DocTypes cannot be customized."))
|
||||
|
||||
if meta.custom:
|
||||
return frappe.msgprint(_("Only standard DocTypes are allowed to be customized from Customize Form."))
|
||||
|
||||
# doctype properties
|
||||
for property in doctype_properties:
|
||||
self.set(property, meta.get(property))
|
||||
|
|
|
|||
|
|
@ -9,28 +9,28 @@ from frappe.core.doctype.doctype.doctype import InvalidFieldNameError
|
|||
test_dependencies = ["Custom Field", "Property Setter"]
|
||||
class TestCustomizeForm(unittest.TestCase):
|
||||
def insert_custom_field(self):
|
||||
frappe.delete_doc_if_exists("Custom Field", "User-test_custom_field")
|
||||
frappe.delete_doc_if_exists("Custom Field", "Event-test_custom_field")
|
||||
frappe.get_doc({
|
||||
"doctype": "Custom Field",
|
||||
"dt": "User",
|
||||
"dt": "Event",
|
||||
"label": "Test Custom Field",
|
||||
"description": "A Custom Field for Testing",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"options": "\nCustom 1\nCustom 2\nCustom 3",
|
||||
"default": "Custom 3",
|
||||
"insert_after": frappe.get_meta('User').fields[-1].fieldname
|
||||
"insert_after": frappe.get_meta('Event').fields[-1].fieldname
|
||||
}).insert()
|
||||
|
||||
def setUp(self):
|
||||
self.insert_custom_field()
|
||||
frappe.db.commit()
|
||||
frappe.clear_cache(doctype="User")
|
||||
frappe.clear_cache(doctype="Event")
|
||||
|
||||
def tearDown(self):
|
||||
frappe.delete_doc("Custom Field", "User-test_custom_field")
|
||||
frappe.delete_doc("Custom Field", "Event-test_custom_field")
|
||||
frappe.db.commit()
|
||||
frappe.clear_cache(doctype="User")
|
||||
frappe.clear_cache(doctype="Event")
|
||||
|
||||
def get_customize_form(self, doctype=None):
|
||||
d = frappe.get_doc("Customize Form")
|
||||
|
|
@ -45,78 +45,67 @@ class TestCustomizeForm(unittest.TestCase):
|
|||
self.assertEqual(len(d.get("fields")), 0)
|
||||
|
||||
d = self.get_customize_form("Event")
|
||||
self.assertEqual(d.doc_type, "Event")
|
||||
self.assertEqual(len(d.get("fields")), 27)
|
||||
self.assertEquals(d.doc_type, "Event")
|
||||
self.assertEquals(len(d.get("fields")), 28)
|
||||
|
||||
d = self.get_customize_form("User")
|
||||
self.assertEqual(d.doc_type, "User")
|
||||
d = self.get_customize_form("Event")
|
||||
self.assertEquals(d.doc_type, "Event")
|
||||
|
||||
self.assertEqual(len(d.get("fields")),
|
||||
len(frappe.get_doc("DocType", d.doc_type).fields) + 1)
|
||||
self.assertEqual(d.get("fields")[-1].fieldname, "test_custom_field")
|
||||
self.assertEqual(d.get("fields", {"fieldname": "location"})[0].in_list_view, 1)
|
||||
self.assertEquals(d.get("fields")[-1].fieldname, "test_custom_field")
|
||||
self.assertEquals(d.get("fields", {"fieldname": "event_type"})[0].in_list_view, 1)
|
||||
|
||||
return d
|
||||
|
||||
def test_save_customization_property(self):
|
||||
d = self.get_customize_form("User")
|
||||
self.assertEqual(frappe.db.get_value("Property Setter",
|
||||
{"doc_type": "User", "property": "allow_copy"}, "value"), None)
|
||||
d = self.get_customize_form("Event")
|
||||
self.assertEquals(frappe.db.get_value("Property Setter",
|
||||
{"doc_type": "Event", "property": "allow_copy"}, "value"), None)
|
||||
|
||||
d.allow_copy = 1
|
||||
d.run_method("save_customization")
|
||||
self.assertEqual(frappe.db.get_value("Property Setter",
|
||||
{"doc_type": "User", "property": "allow_copy"}, "value"), '1')
|
||||
self.assertEquals(frappe.db.get_value("Property Setter",
|
||||
{"doc_type": "Event", "property": "allow_copy"}, "value"), '1')
|
||||
|
||||
d.allow_copy = 0
|
||||
d.run_method("save_customization")
|
||||
self.assertEqual(frappe.db.get_value("Property Setter",
|
||||
{"doc_type": "User", "property": "allow_copy"}, "value"), None)
|
||||
self.assertEquals(frappe.db.get_value("Property Setter",
|
||||
{"doc_type": "Event", "property": "allow_copy"}, "value"), None)
|
||||
|
||||
def test_save_customization_field_property(self):
|
||||
d = self.get_customize_form("User")
|
||||
self.assertEqual(frappe.db.get_value("Property Setter",
|
||||
{"doc_type": "User", "property": "reqd", "field_name": "location"}, "value"), None)
|
||||
d = self.get_customize_form("Event")
|
||||
self.assertEquals(frappe.db.get_value("Property Setter",
|
||||
{"doc_type": "Event", "property": "reqd", "field_name": "repeat_this_event"}, "value"), None)
|
||||
|
||||
location_field = d.get("fields", {"fieldname": "location"})[0]
|
||||
location_field.reqd = 1
|
||||
repeat_this_event_field = d.get("fields", {"fieldname": "repeat_this_event"})[0]
|
||||
repeat_this_event_field.reqd = 1
|
||||
d.run_method("save_customization")
|
||||
self.assertEqual(frappe.db.get_value("Property Setter",
|
||||
{"doc_type": "User", "property": "reqd", "field_name": "location"}, "value"), '1')
|
||||
self.assertEquals(frappe.db.get_value("Property Setter",
|
||||
{"doc_type": "Event", "property": "reqd", "field_name": "repeat_this_event"}, "value"), '1')
|
||||
|
||||
location_field = d.get("fields", {"fieldname": "location"})[0]
|
||||
location_field.reqd = 0
|
||||
repeat_this_event_field = d.get("fields", {"fieldname": "repeat_this_event"})[0]
|
||||
repeat_this_event_field.reqd = 0
|
||||
d.run_method("save_customization")
|
||||
self.assertEqual(frappe.db.get_value("Property Setter",
|
||||
{"doc_type": "User", "property": "reqd", "field_name": "location"}, "value"), None)
|
||||
|
||||
# for not allowing to change mandatory property of standard fields
|
||||
self.assertEqual(frappe.db.get_value("Property Setter",
|
||||
{"doc_type": "User", "property": "reqd", "field_name": "email"}, "value"), None)
|
||||
|
||||
email_field = d.get("fields", {"fieldname": "email"})[0]
|
||||
email_field.reqd = 0
|
||||
d.run_method("save_customization")
|
||||
|
||||
self.assertEqual(frappe.db.get_value("Property Setter",
|
||||
{"doc_type": "User", "property": "reqd", "field_name": "email"}, "value"), None)
|
||||
self.assertEquals(frappe.db.get_value("Property Setter",
|
||||
{"doc_type": "Event", "property": "reqd", "field_name": "repeat_this_event"}, "value"), None)
|
||||
|
||||
def test_save_customization_custom_field_property(self):
|
||||
d = self.get_customize_form("User")
|
||||
self.assertEqual(frappe.db.get_value("Custom Field", "User-test_custom_field", "reqd"), 0)
|
||||
d = self.get_customize_form("Event")
|
||||
self.assertEquals(frappe.db.get_value("Custom Field", "Event-test_custom_field", "reqd"), 0)
|
||||
|
||||
custom_field = d.get("fields", {"fieldname": "test_custom_field"})[0]
|
||||
custom_field.reqd = 1
|
||||
d.run_method("save_customization")
|
||||
self.assertEqual(frappe.db.get_value("Custom Field", "User-test_custom_field", "reqd"), 1)
|
||||
self.assertEquals(frappe.db.get_value("Custom Field", "Event-test_custom_field", "reqd"), 1)
|
||||
|
||||
custom_field = d.get("fields", {"is_custom_field": True})[0]
|
||||
custom_field.reqd = 0
|
||||
d.run_method("save_customization")
|
||||
self.assertEqual(frappe.db.get_value("Custom Field", "User-test_custom_field", "reqd"), 0)
|
||||
self.assertEquals(frappe.db.get_value("Custom Field", "Event-test_custom_field", "reqd"), 0)
|
||||
|
||||
def test_save_customization_new_field(self):
|
||||
d = self.get_customize_form("User")
|
||||
d = self.get_customize_form("Event")
|
||||
last_fieldname = d.fields[-1].fieldname
|
||||
d.append("fields", {
|
||||
"label": "Test Add Custom Field Via Customize Form",
|
||||
|
|
@ -124,19 +113,19 @@ class TestCustomizeForm(unittest.TestCase):
|
|||
"is_custom_field": 1
|
||||
})
|
||||
d.run_method("save_customization")
|
||||
self.assertEqual(frappe.db.get_value("Custom Field",
|
||||
"User-test_add_custom_field_via_customize_form", "fieldtype"), "Data")
|
||||
self.assertEquals(frappe.db.get_value("Custom Field",
|
||||
"Event-test_add_custom_field_via_customize_form", "fieldtype"), "Data")
|
||||
|
||||
self.assertEqual(frappe.db.get_value("Custom Field",
|
||||
"User-test_add_custom_field_via_customize_form", 'insert_after'), last_fieldname)
|
||||
self.assertEquals(frappe.db.get_value("Custom Field",
|
||||
"Event-test_add_custom_field_via_customize_form", 'insert_after'), last_fieldname)
|
||||
|
||||
frappe.delete_doc("Custom Field", "User-test_add_custom_field_via_customize_form")
|
||||
self.assertEqual(frappe.db.get_value("Custom Field",
|
||||
"User-test_add_custom_field_via_customize_form"), None)
|
||||
frappe.delete_doc("Custom Field", "Event-test_add_custom_field_via_customize_form")
|
||||
self.assertEquals(frappe.db.get_value("Custom Field",
|
||||
"Event-test_add_custom_field_via_customize_form"), None)
|
||||
|
||||
|
||||
def test_save_customization_remove_field(self):
|
||||
d = self.get_customize_form("User")
|
||||
d = self.get_customize_form("Event")
|
||||
custom_field = d.get("fields", {"fieldname": "test_custom_field"})[0]
|
||||
d.get("fields").remove(custom_field)
|
||||
d.run_method("save_customization")
|
||||
|
|
@ -148,24 +137,24 @@ class TestCustomizeForm(unittest.TestCase):
|
|||
|
||||
def test_reset_to_defaults(self):
|
||||
d = frappe.get_doc("Customize Form")
|
||||
d.doc_type = "User"
|
||||
d.doc_type = "Event"
|
||||
d.run_method('reset_to_defaults')
|
||||
|
||||
self.assertEqual(d.get("fields", {"fieldname": "location"})[0].in_list_view, 0)
|
||||
self.assertEquals(d.get("fields", {"fieldname": "repeat_this_event"})[0].in_list_view, 0)
|
||||
|
||||
frappe.local.test_objects["Property Setter"] = []
|
||||
make_test_records_for_doctype("Property Setter")
|
||||
|
||||
def test_set_allow_on_submit(self):
|
||||
d = self.get_customize_form("User")
|
||||
d.get("fields", {"fieldname": "first_name"})[0].allow_on_submit = 1
|
||||
d = self.get_customize_form("Event")
|
||||
d.get("fields", {"fieldname": "subject"})[0].allow_on_submit = 1
|
||||
d.get("fields", {"fieldname": "test_custom_field"})[0].allow_on_submit = 1
|
||||
d.run_method("save_customization")
|
||||
|
||||
d = self.get_customize_form("User")
|
||||
d = self.get_customize_form("Event")
|
||||
|
||||
# don't allow for standard fields
|
||||
self.assertEqual(d.get("fields", {"fieldname": "first_name"})[0].allow_on_submit or 0, 0)
|
||||
self.assertEquals(d.get("fields", {"fieldname": "subject"})[0].allow_on_submit or 0, 0)
|
||||
|
||||
# allow for custom field
|
||||
self.assertEqual(d.get("fields", {"fieldname": "test_custom_field"})[0].allow_on_submit, 1)
|
||||
|
|
@ -193,4 +182,12 @@ class TestCustomizeForm(unittest.TestCase):
|
|||
|
||||
# undo
|
||||
df.default = None
|
||||
d.run_method("save_customization")
|
||||
d.run_method("save_customization")
|
||||
|
||||
def test_core_doctype_customization(self):
|
||||
d = self.get_customize_form('User')
|
||||
e = self.get_customize_form('Custom Field')
|
||||
|
||||
# core doctype is invalid, hence no attributes are set
|
||||
self.assertEquals(d.get("fields"), [])
|
||||
self.assertEquals(e.get("fields"), [])
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -37,12 +37,16 @@ class PropertySetter(Document):
|
|||
and property = %(property)s""", self.get_valid_dict())
|
||||
|
||||
def get_property_list(self, dt):
|
||||
return frappe.db.sql("""select fieldname, label, fieldtype
|
||||
from tabDocField
|
||||
where parent=%s
|
||||
and fieldtype not in ('Section Break', 'Column Break', 'HTML', 'Read Only', 'Table', 'Fold')
|
||||
and coalesce(fieldname, '') != ''
|
||||
order by label asc""", dt, as_dict=1)
|
||||
return frappe.db.get_all('DocField',
|
||||
fields=['fieldname', 'label', 'fieldtype'],
|
||||
filters={
|
||||
'parent': dt,
|
||||
'fieldtype': ['not in', ('Section Break', 'Column Break', 'HTML', 'Read Only', 'Fold') + frappe.model.table_fields],
|
||||
'fieldname': ['!=', '']
|
||||
},
|
||||
order_by='label asc',
|
||||
as_dict=1
|
||||
)
|
||||
|
||||
def get_setup_data(self):
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from __future__ import unicode_literals
|
||||
from six import with_metaclass
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from frappe.utils.password import get_decrypted_password
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ from time import time
|
|||
from frappe.utils import now, getdate, cast_fieldtype
|
||||
from frappe.utils.background_jobs import execute_job, get_queue
|
||||
from frappe.model.utils.link_count import flush_local_link_count
|
||||
from frappe.utils import cint
|
||||
|
||||
# imports - compatibility imports
|
||||
from six import (
|
||||
|
|
@ -538,9 +539,9 @@ class Database(object):
|
|||
`tabSingles` where `doctype`=%s and `field`=%s""", (doctype, fieldname))
|
||||
val = val[0][0] if val else None
|
||||
|
||||
if val=="0" or val=="1":
|
||||
# check type
|
||||
val = int(val)
|
||||
df = frappe.get_meta(doctype).get_field(fieldname)
|
||||
if df.fieldtype in frappe.model.numeric_fieldtypes:
|
||||
val = cint(val)
|
||||
|
||||
self.value_cache[doctype][fieldname] = val
|
||||
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ CREATE TABLE `tabDocPerm` (
|
|||
|
||||
DROP TABLE IF EXISTS `tabDocType`;
|
||||
CREATE TABLE `tabDocType` (
|
||||
`name` varchar(255) NOT NULL DEFAULT '',
|
||||
`name` varchar(255) NOT NULL,
|
||||
`creation` datetime(6) DEFAULT NULL,
|
||||
`modified` datetime(6) DEFAULT NULL,
|
||||
`modified_by` varchar(255) DEFAULT NULL,
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ create index on "tabDocPerm" ("parent");
|
|||
|
||||
DROP TABLE IF EXISTS "tabDocType";
|
||||
CREATE TABLE "tabDocType" (
|
||||
"name" varchar(255) NOT NULL DEFAULT '',
|
||||
"name" varchar(255) NOT NULL,
|
||||
"creation" timestamp(6) DEFAULT NULL,
|
||||
"modified" timestamp(6) DEFAULT NULL,
|
||||
"modified_by" varchar(255) DEFAULT NULL,
|
||||
|
|
|
|||
|
|
@ -307,7 +307,7 @@ def make_user_copy(module_name, user):
|
|||
'module_name': module_name
|
||||
})
|
||||
|
||||
for key in ('app', 'label', 'route', 'type', '_doctype', 'idx', 'reverse', 'force_show'):
|
||||
for key in ('app', 'label', 'route', 'type', '_doctype', 'idx', 'reverse', 'force_show', 'link', 'icon', 'color'):
|
||||
if original.get(key):
|
||||
desktop_icon.set(key, original.get(key))
|
||||
|
||||
|
|
@ -410,7 +410,7 @@ def get_user_icons(user):
|
|||
add = False
|
||||
|
||||
if not icon.custom:
|
||||
if icon.module_name=='Learn':
|
||||
if icon.module_name==['Help', 'Settings']:
|
||||
pass
|
||||
|
||||
elif icon.type=="page" and icon.link not in allowed_pages:
|
||||
|
|
|
|||
|
|
@ -92,7 +92,6 @@ def get_permission_query_conditions(user):
|
|||
}
|
||||
|
||||
def has_permission(doc, user):
|
||||
frappe.log_error(doc.owner)
|
||||
if doc.event_type=="Public" or doc.owner==user:
|
||||
return True
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import json
|
|||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from six import iteritems
|
||||
from frappe.custom.doctype.custom_field.custom_field import create_custom_field
|
||||
|
||||
|
||||
class KanbanBoard(Document):
|
||||
|
|
@ -130,17 +129,8 @@ def update_order(board_name, order):
|
|||
@frappe.whitelist()
|
||||
def quick_kanban_board(doctype, board_name, field_name, project=None):
|
||||
'''Create new KanbanBoard quickly with default options'''
|
||||
|
||||
doc = frappe.new_doc('Kanban Board')
|
||||
|
||||
if field_name == 'kanban_column':
|
||||
create_custom_field(doctype, {
|
||||
'label': 'Kanban Column',
|
||||
'fieldname': 'kanban_column',
|
||||
'fieldtype': 'Select',
|
||||
'hidden': 1,
|
||||
'owner': 'Administrator'
|
||||
})
|
||||
|
||||
meta = frappe.get_meta(doctype)
|
||||
|
||||
options = ''
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors and Contributors
|
||||
# See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
|
|
|
|||
|
|
@ -42,9 +42,10 @@ def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None):
|
|||
link_meta_bundle = frappe.desk.form.load.get_meta_bundle(dt)
|
||||
linkmeta = link_meta_bundle[0]
|
||||
if not linkmeta.get("issingle"):
|
||||
fields = [d.fieldname for d in linkmeta.get("fields", {"in_list_view":1,
|
||||
"fieldtype": ["not in", ["Image", "HTML", "Button", "Table"]]})] \
|
||||
+ ["name", "modified", "docstatus"]
|
||||
fields = [d.fieldname for d in linkmeta.get("fields", {
|
||||
"in_list_view": 1,
|
||||
"fieldtype": ["not in", ("Image", "HTML", "Button") + frappe.model.table_fields]
|
||||
})] + ["name", "modified", "docstatus"]
|
||||
|
||||
if link.get("add_fields"):
|
||||
fields += link["add_fields"]
|
||||
|
|
@ -116,7 +117,7 @@ def _get_linked_doctypes(doctype, without_ignore_user_permissions_enabled=False)
|
|||
ret.update(get_linked_fields(doctype, without_ignore_user_permissions_enabled))
|
||||
ret.update(get_dynamic_linked_fields(doctype, without_ignore_user_permissions_enabled))
|
||||
|
||||
filters=[['fieldtype','=','Table'], ['options', '=', doctype]]
|
||||
filters=[['fieldtype', 'in', frappe.model.table_fields], ['options', '=', doctype]]
|
||||
if without_ignore_user_permissions_enabled: filters.append(['ignore_user_permissions', '!=', 1])
|
||||
# find links of parents
|
||||
links = frappe.get_all("DocField", fields=["parent as dt"], filters=filters)
|
||||
|
|
@ -159,7 +160,7 @@ def get_linked_fields(doctype, without_ignore_user_permissions_enabled=False):
|
|||
for doctype_name in links_dict:
|
||||
ret[doctype_name] = { "fieldname": links_dict.get(doctype_name) }
|
||||
table_doctypes = frappe.get_all("DocType", filters=[["istable", "=", "1"], ["name", "in", tuple(links_dict)]])
|
||||
child_filters = [['fieldtype','=', 'Table'], ['options', 'in', tuple(doctype.name for doctype in table_doctypes)]]
|
||||
child_filters = [['fieldtype','in', frappe.model.table_fields], ['options', 'in', tuple(doctype.name for doctype in table_doctypes)]]
|
||||
if without_ignore_user_permissions_enabled: child_filters.append(['ignore_user_permissions', '!=', 1])
|
||||
|
||||
# find out if linked in a child table
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ def getdoctype(doctype, with_parent=False, cached_timestamp=None):
|
|||
def get_meta_bundle(doctype):
|
||||
bundle = [frappe.desk.form.meta.get_meta(doctype)]
|
||||
for df in bundle[0].fields:
|
||||
if df.fieldtype=="Table":
|
||||
if df.fieldtype in frappe.model.table_fields:
|
||||
bundle.append(frappe.desk.form.meta.get_meta(df.options, not frappe.conf.developer_mode))
|
||||
return bundle
|
||||
|
||||
|
|
@ -219,11 +219,11 @@ def get_view_logs(doctype, docname):
|
|||
""" get and return the latest view logs if available """
|
||||
logs = []
|
||||
if hasattr(frappe.get_meta(doctype), 'track_views') and frappe.get_meta(doctype).track_views:
|
||||
view_logs = frappe.get_all("View log", filters={
|
||||
view_logs = frappe.get_all("View Log", filters={
|
||||
"reference_doctype": doctype,
|
||||
"reference_name": docname,
|
||||
}, fields=["name", "creation"], order_by="creation desc")
|
||||
}, fields=["name", "creation", "owner"], order_by="creation desc")
|
||||
|
||||
if view_logs:
|
||||
if view_logs:
|
||||
logs = view_logs
|
||||
return logs
|
||||
|
|
|
|||
|
|
@ -12,8 +12,15 @@ import json
|
|||
@frappe.whitelist()
|
||||
@frappe.read_only()
|
||||
def get_notifications():
|
||||
if frappe.flags.in_install:
|
||||
return
|
||||
if (frappe.flags.in_install or
|
||||
not frappe.db.get_single_value('System Settings', 'setup_complete')):
|
||||
return {
|
||||
"open_count_doctype": {},
|
||||
"open_count_module": {},
|
||||
"open_count_other": {},
|
||||
"targets": {},
|
||||
"new_messages": []
|
||||
}
|
||||
|
||||
config = get_notification_config()
|
||||
|
||||
|
|
@ -108,7 +115,8 @@ def get_notifications_for_doctypes(config, notification_count):
|
|||
|
||||
except Exception as e:
|
||||
# OperationalError: (1412, 'Table definition has changed, please retry transaction')
|
||||
if e.args[0]!=1412:
|
||||
# InternalError: (1684, 'Table definition is being modified by concurrent DDL statement')
|
||||
if e.args[0] not in (1412, 1684):
|
||||
raise
|
||||
|
||||
else:
|
||||
|
|
@ -148,7 +156,7 @@ def get_notifications_for_targets(config, notification_percent):
|
|||
frappe.clear_messages()
|
||||
pass
|
||||
except Exception as e:
|
||||
if e.args[0]!=1412:
|
||||
if e.args[0] not in (1412, 1684):
|
||||
raise
|
||||
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from __future__ import unicode_literals
|
||||
import os
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
|
|
|||
|
|
@ -220,9 +220,6 @@ def runquery(q='', ret=0, from_export=0):
|
|||
def runquery_csv():
|
||||
global out
|
||||
|
||||
# run query
|
||||
res = runquery(from_export = 1)
|
||||
|
||||
q = frappe.form_dict.get('query')
|
||||
|
||||
rep_name = frappe.form_dict.get('report_name')
|
||||
|
|
@ -234,9 +231,6 @@ def runquery_csv():
|
|||
|
||||
if not rep_name: rep_name = 'DataExport'
|
||||
|
||||
# Headings
|
||||
heads = []
|
||||
|
||||
rows = [[rep_name], out['colnames']] + out['values']
|
||||
|
||||
from six import StringIO
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ def generate_report_result(report, filters=None, user=None):
|
|||
module = report.module or frappe.db.get_value("DocType", report.ref_doctype, "module")
|
||||
if report.is_standard == "Yes":
|
||||
method_name = get_report_module_dotted_path(module, report.name) + ".execute"
|
||||
threshold = 10
|
||||
threshold = 30
|
||||
res = []
|
||||
|
||||
start_time = datetime.datetime.now()
|
||||
|
|
@ -66,9 +66,13 @@ def generate_report_result(report, filters=None, user=None):
|
|||
|
||||
end_time = datetime.datetime.now()
|
||||
|
||||
if (end_time - start_time).seconds > threshold and not report.prepared_report:
|
||||
execution_time = (end_time - start_time).seconds
|
||||
|
||||
if execution_time > threshold and not report.prepared_report:
|
||||
report.db_set('prepared_report', 1)
|
||||
|
||||
frappe.cache().hset('report_execution_time', report.name, execution_time)
|
||||
|
||||
columns, result = res[0], res[1]
|
||||
if len(res) > 2:
|
||||
message = res[2]
|
||||
|
|
@ -89,7 +93,8 @@ def generate_report_result(report, filters=None, user=None):
|
|||
"message": message,
|
||||
"chart": chart,
|
||||
"data_to_be_printed": data_to_be_printed,
|
||||
"status": status
|
||||
"status": status,
|
||||
"execution_time": frappe.cache().hget('report_execution_time', report.name) or 0
|
||||
}
|
||||
|
||||
@frappe.whitelist()
|
||||
|
|
@ -147,7 +152,8 @@ def get_script(report_name):
|
|||
|
||||
return {
|
||||
"script": render_include(script),
|
||||
"html_format": html_format
|
||||
"html_format": html_format,
|
||||
"execution_time": frappe.cache().hget('report_execution_time', report_name) or 0
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -164,7 +170,7 @@ def run(report_name, filters=None, user=None):
|
|||
|
||||
result = None
|
||||
|
||||
if report.prepared_report:
|
||||
if report.prepared_report and not report.disable_prepared_report:
|
||||
if filters:
|
||||
if isinstance(filters, string_types):
|
||||
filters = json.loads(filters)
|
||||
|
|
@ -185,7 +191,9 @@ def run(report_name, filters=None, user=None):
|
|||
def get_prepared_report_result(report, filters, dn="", user=None):
|
||||
latest_report_data = {}
|
||||
# Only look for completed prepared reports with given filters.
|
||||
doc_list = frappe.get_all("Prepared Report", filters={"status": "Completed", "report_name": report.name, "filters": json.dumps(filters), "owner": user})
|
||||
doc_list = frappe.get_all("Prepared Report",
|
||||
filters={"status": "Completed", "report_name": report.name, "filters": filters, "owner": user})
|
||||
|
||||
doc = None
|
||||
if len(doc_list):
|
||||
if dn:
|
||||
|
|
@ -254,7 +262,7 @@ def export_query():
|
|||
if row and (i in visible_idx):
|
||||
row_list = []
|
||||
for idx in range(len(data.columns)):
|
||||
row_list.append(row.get(columns[idx]["fieldname"],""))
|
||||
row_list.append(row.get(columns[idx]["fieldname"], row.get(columns[idx]["label"], "")))
|
||||
result.append(row_list)
|
||||
elif not row:
|
||||
result.append([])
|
||||
|
|
|
|||
|
|
@ -42,7 +42,6 @@ def get_form_params():
|
|||
else:
|
||||
data["save_user_settings"] = True
|
||||
|
||||
doctype = data["doctype"]
|
||||
fields = data["fields"]
|
||||
|
||||
for field in fields:
|
||||
|
|
@ -213,7 +212,7 @@ def delete_items():
|
|||
"""delete selected items"""
|
||||
import json
|
||||
|
||||
il = sorted(json.loads(frappe.form_dict.get('items')), reverse=True)
|
||||
il = sorted(json.loads(frappe.form_dict.get('items')), reverse=True, key=frappe.safe_decode)
|
||||
doctype = frappe.form_dict.get('doctype')
|
||||
|
||||
failed = []
|
||||
|
|
|
|||
|
|
@ -74,6 +74,8 @@ class AutoEmailReport(Document):
|
|||
return None
|
||||
|
||||
if self.format == 'HTML':
|
||||
columns, data = make_links(columns, data)
|
||||
|
||||
return self.get_html_table(columns, data)
|
||||
|
||||
elif self.format == 'XLSX':
|
||||
|
|
@ -195,3 +197,15 @@ def send_monthly():
|
|||
'''Check reports to be sent monthly'''
|
||||
for report in frappe.get_all('Auto Email Report', {'enabled': 1, 'frequency': 'Monthly'}):
|
||||
frappe.get_doc('Auto Email Report', report.name).send()
|
||||
|
||||
def make_links(columns, data):
|
||||
for row in data:
|
||||
for col in columns:
|
||||
if col.fieldtype == "Link" and col.options != "Currency":
|
||||
if col.options and row[col.fieldname]:
|
||||
row[col.fieldname] = get_link_to_form(col.options, row[col.fieldname])
|
||||
elif col.fieldtype == "Dynamic Link":
|
||||
if col.options and row[col.fieldname]:
|
||||
row[col.fieldname] = get_link_to_form(row[col.options], row[col.fieldname])
|
||||
|
||||
return columns, data
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from __future__ import unicode_literals
|
|||
|
||||
import frappe
|
||||
import unittest, json
|
||||
from frappe.utils import get_link_to_form
|
||||
|
||||
# test_records = frappe.get_test_records('Auto Email Report')
|
||||
|
||||
|
|
@ -25,8 +26,8 @@ class TestAutoEmailReport(unittest.TestCase):
|
|||
)).insert()
|
||||
|
||||
data = auto_email_report.get_report_content()
|
||||
self.assertTrue('<td>DocShare</td>' in data)
|
||||
self.assertTrue('<td>Core</td>' in data)
|
||||
|
||||
self.assertTrue('<td>'+str(get_link_to_form('Module Def', 'Core'))+'</td>' in data)
|
||||
|
||||
auto_email_report.format = 'CSV'
|
||||
|
||||
|
|
|
|||
|
|
@ -1130,6 +1130,39 @@
|
|||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "0",
|
||||
"fieldname": "no_smtp_authentication",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Disable SMTP server authentication",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"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,
|
||||
|
|
@ -1530,7 +1563,7 @@
|
|||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-08-01 19:37:43.234891",
|
||||
"modified": "2019-01-30 11:02:41.011412",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Email",
|
||||
"name": "Email Account",
|
||||
|
|
@ -1566,4 +1599,4 @@
|
|||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ class EmailAccount(Document):
|
|||
if self.enable_outgoing:
|
||||
self.check_smtp()
|
||||
else:
|
||||
if self.enable_incoming or self.enable_outgoing:
|
||||
if self.enable_incoming or (self.enable_outgoing and not self.no_smtp_authentication):
|
||||
frappe.throw(_("Password is required or select Awaiting Password"))
|
||||
|
||||
if self.notify_if_unreplied:
|
||||
|
|
@ -134,8 +134,9 @@ class EmailAccount(Document):
|
|||
port = cint(self.smtp_port),
|
||||
use_tls = cint(self.use_tls)
|
||||
)
|
||||
if self.password:
|
||||
if self.password and not self.no_smtp_authentication:
|
||||
server.password = self.get_password()
|
||||
|
||||
server.sess
|
||||
|
||||
def get_incoming_server(self, in_receive=False, email_sync_rule="UNSEEN"):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe, os
|
||||
import unittest, email
|
||||
|
||||
|
|
|
|||
|
|
@ -559,24 +559,55 @@
|
|||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "0",
|
||||
"fieldname": "retry",
|
||||
"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": "Retry",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "fa fa-envelope",
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 1,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-11-09 15:34:07.229657",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Email",
|
||||
"name": "Email Queue",
|
||||
"owner": "Administrator",
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "fa fa-envelope",
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 1,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-01-11 09:05:04.175368",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Email",
|
||||
"name": "Email Queue",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import frappe, re, os
|
|||
from frappe.utils.pdf import get_pdf
|
||||
from frappe.email.smtp import get_outgoing_email_account
|
||||
from frappe.utils import (get_url, scrub_urls, strip, expand_relative_urls, cint,
|
||||
split_emails, to_markdown, markdown, encode, random_string, parse_addr)
|
||||
split_emails, to_markdown, markdown, random_string, parse_addr)
|
||||
import email.utils
|
||||
from six import iteritems, text_type, string_types
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
import json
|
||||
|
||||
|
|
|
|||
|
|
@ -382,7 +382,7 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False, from_test=Fals
|
|||
email = frappe.db.sql('''select
|
||||
name, status, communication, message, sender, reference_doctype,
|
||||
reference_name, unsubscribe_param, unsubscribe_method, expose_recipients,
|
||||
show_as_cc, add_unsubscribe_link, attachments
|
||||
show_as_cc, add_unsubscribe_link, attachments, retry
|
||||
from
|
||||
`tabEmail Queue`
|
||||
where
|
||||
|
|
@ -464,12 +464,16 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False, from_test=Fals
|
|||
except Exception as e:
|
||||
frappe.db.rollback()
|
||||
|
||||
if any("Sent" == s.status for s in recipients_list):
|
||||
frappe.db.sql("""update `tabEmail Queue` set status='Partially Errored', error=%s where name=%s""",
|
||||
(text_type(e), email.name), auto_commit=auto_commit)
|
||||
if email.retry < 3:
|
||||
frappe.db.sql("""update `tabEmail Queue` set status='Not Sent', modified=%s, retry=retry+1 where name=%s""",
|
||||
(now_datetime(), email.name), auto_commit=auto_commit)
|
||||
else:
|
||||
frappe.db.sql("""update `tabEmail Queue` set status='Error', error=%s
|
||||
where name=%s""", (text_type(e), email.name), auto_commit=auto_commit)
|
||||
if any("Sent" == s.status for s in recipients_list):
|
||||
frappe.db.sql("""update `tabEmail Queue` set status='Partially Errored', error=%s where name=%s""",
|
||||
(text_type(e), email.name), auto_commit=auto_commit)
|
||||
else:
|
||||
frappe.db.sql("""update `tabEmail Queue` set status='Error', error=%s
|
||||
where name=%s""", (text_type(e), email.name), auto_commit=auto_commit)
|
||||
|
||||
if email.communication:
|
||||
frappe.get_doc('Communication', email.communication).set_delivery_status(commit=auto_commit)
|
||||
|
|
|
|||
|
|
@ -553,6 +553,7 @@ class Email:
|
|||
|
||||
# fix due to a python bug in poplib that limits it to 2048
|
||||
poplib._MAXLINE = 20480
|
||||
imaplib._MAXLINE = 20480
|
||||
|
||||
class TimerMixin(object):
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ def get_outgoing_email_account(raise_exception_not_set=True, append_to=None, sen
|
|||
if email_account:
|
||||
if email_account.enable_outgoing and not getattr(email_account, 'from_site_config', False):
|
||||
raise_exception = True
|
||||
if email_account.smtp_server in ['localhost','127.0.0.1']:
|
||||
if email_account.smtp_server in ['localhost','127.0.0.1'] or email_account.no_smtp_authentication:
|
||||
raise_exception = False
|
||||
email_account.password = email_account.get_password(raise_exception=raise_exception)
|
||||
email_account.default_sender = email.utils.formataddr((email_account.name, email_account.get("email_id")))
|
||||
|
|
@ -170,11 +170,14 @@ class SMTPServer:
|
|||
self.email_account = get_outgoing_email_account(raise_exception_not_set=False, append_to=append_to, sender=sender)
|
||||
if self.email_account:
|
||||
self.server = self.email_account.smtp_server
|
||||
self.login = getattr(self.email_account, "login_id", None) or self.email_account.email_id
|
||||
if self.email_account.ascii_encode_password:
|
||||
self.password = frappe.safe_encode(self.email_account.password, 'ascii')
|
||||
self.login = (getattr(self.email_account, "login_id", None) or self.email_account.email_id)
|
||||
if not self.email_account.no_smtp_authentication:
|
||||
if self.email_account.ascii_encode_password:
|
||||
self.password = frappe.safe_encode(self.email_account.password, 'ascii')
|
||||
else:
|
||||
self.password = self.email_account.password
|
||||
else:
|
||||
self.password = self.email_account.password
|
||||
self.password = None
|
||||
self.port = self.email_account.smtp_port
|
||||
self.use_tls = self.email_account.use_tls
|
||||
self.sender = self.email_account.email_id
|
||||
|
|
@ -210,7 +213,7 @@ class SMTPServer:
|
|||
self._sess.ehlo()
|
||||
|
||||
if self.login and self.password:
|
||||
ret = self._sess.login((self.login or ""), (self.password or ""))
|
||||
ret = self._sess.login(str(self.login or ""), str(self.password or ""))
|
||||
|
||||
# check if logged correctly
|
||||
if ret[0]!=235:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# License: GNU General Public License v3. See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe, unittest, os, base64
|
||||
import unittest, os, base64
|
||||
from frappe.email.email_body import (replace_filename_with_cid,
|
||||
get_email, inline_style_in_html, get_header)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from __future__ import print_function
|
||||
from __future__ import print_function, unicode_literals
|
||||
import requests
|
||||
import json
|
||||
import frappe
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: See license.txt
|
||||
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
test_records = frappe.get_test_records('Country')
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue