Merge branch 'develop' of https://github.com/frappe/frappe into develop
This commit is contained in:
commit
b1c63dbe95
278 changed files with 8992 additions and 14584 deletions
|
|
@ -140,6 +140,7 @@
|
|||
"expect": true,
|
||||
"context": true,
|
||||
"before": true,
|
||||
"beforeEach": true
|
||||
"beforeEach": true,
|
||||
"qz": true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
29
MANIFEST.in
29
MANIFEST.in
|
|
@ -1,29 +0,0 @@
|
|||
include MANIFEST.in
|
||||
include requirements.txt
|
||||
include *.json
|
||||
include *.md
|
||||
include *.py
|
||||
recursive-include frappe *.css
|
||||
recursive-include frappe *.dat
|
||||
recursive-include frappe *.eot
|
||||
recursive-include frappe *.gif
|
||||
recursive-include frappe *.html
|
||||
recursive-include frappe *.jpg
|
||||
recursive-include frappe *.js
|
||||
recursive-include frappe *.json
|
||||
recursive-include frappe *.md
|
||||
recursive-include frappe *.otf
|
||||
recursive-include frappe *.png
|
||||
recursive-include frappe *.py
|
||||
recursive-include frappe *.sql
|
||||
recursive-include frappe *.svg
|
||||
recursive-include frappe *.swf
|
||||
recursive-include frappe *.ttf
|
||||
recursive-include frappe *.woff
|
||||
recursive-include frappe *.xml
|
||||
recursive-include frappe *.csv
|
||||
recursive-include frappe *.ico
|
||||
recursive-include frappe *.less
|
||||
recursive-include frappe *.txt
|
||||
recursive-include frappe/public *
|
||||
recursive-exclude * *.pyc
|
||||
61
cypress/integration/file_uploader.js
Normal file
61
cypress/integration/file_uploader.js
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
context('FileUploader', () => {
|
||||
before(() => {
|
||||
cy.login('Administrator', 'qwe');
|
||||
cy.visit('/desk');
|
||||
});
|
||||
|
||||
function open_upload_dialog() {
|
||||
cy.window().its('frappe').then(frappe => {
|
||||
new frappe.ui.FileUploader();
|
||||
});
|
||||
}
|
||||
|
||||
it('upload dialog api works', () => {
|
||||
open_upload_dialog();
|
||||
cy.get_open_dialog().should('contain', 'Drag and drop files');
|
||||
cy.hide_dialog();
|
||||
});
|
||||
|
||||
it('should accept dropped files', () => {
|
||||
open_upload_dialog();
|
||||
|
||||
cy.fixture('example.json').then(fileContent => {
|
||||
cy.get_open_dialog().find('.file-upload-area').upload(
|
||||
{ fileContent, fileName: 'example.json', mimeType: 'application/json' },
|
||||
{ subjectType: 'drag-n-drop' },
|
||||
);
|
||||
cy.get_open_dialog().find('.file-info').should('contain', 'example.json');
|
||||
cy.server();
|
||||
cy.route('POST', '/api/method/upload_file').as('upload_file');
|
||||
cy.get_open_dialog().find('.btn-primary').click();
|
||||
cy.wait('@upload_file').its('status').should('be', 200);
|
||||
cy.get('.modal:visible').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
||||
it('should accept uploaded files', () => {
|
||||
open_upload_dialog();
|
||||
|
||||
cy.get_open_dialog().find('a:contains("uploaded file")').click();
|
||||
cy.get_open_dialog().find('.tree-label:contains("example.json")').first().click();
|
||||
cy.server();
|
||||
cy.route('POST', '/api/method/upload_file').as('upload_file');
|
||||
cy.get_open_dialog().find('.btn-primary').click();
|
||||
cy.wait('@upload_file').its('response.body.message')
|
||||
.should('have.property', 'file_url', '/private/files/example.json');
|
||||
cy.get('.modal:visible').should('not.exist');
|
||||
});
|
||||
|
||||
it('should accept web links', () => {
|
||||
open_upload_dialog();
|
||||
|
||||
cy.get_open_dialog().find('a:contains("web link")').click();
|
||||
cy.get_open_dialog().find('.file-web-link input').type('https://github.com');
|
||||
cy.server();
|
||||
cy.route('POST', '/api/method/upload_file').as('upload_file');
|
||||
cy.get_open_dialog().find('.btn-primary').click();
|
||||
cy.wait('@upload_file').its('response.body.message')
|
||||
.should('have.property', 'file_url', 'https://github.com');
|
||||
cy.get('.modal:visible').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
|
@ -9,7 +9,7 @@ context('Table MultiSelect', () => {
|
|||
cy.new_form('Assignment Rule');
|
||||
cy.fill_field('__newname', name);
|
||||
cy.fill_field('document_type', 'ToDo');
|
||||
cy.fill_field('assign_condition', 'status=="Open"');
|
||||
cy.fill_field('assign_condition', 'status=="Open"', 'Code');
|
||||
cy.get('input[data-fieldname="users"]').focus().as('input');
|
||||
cy.get('input[data-fieldname="users"] + ul').should('be.visible');
|
||||
cy.get('@input').type('test{enter}', { delay: 100 });
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import 'cypress-file-upload';
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
|
|
@ -38,7 +39,10 @@ Cypress.Commands.add('fill_field', (fieldname, value, fieldtype='Data') => {
|
|||
let selector = `.form-control[data-fieldname="${fieldname}"]`;
|
||||
|
||||
if (fieldtype === 'Text Editor') {
|
||||
selector = `[data-fieldname="${fieldname}"] .ql-editor`;
|
||||
selector = `[data-fieldname="${fieldname}"] .ql-editor[contenteditable=true]`;
|
||||
}
|
||||
if (fieldtype === 'Code') {
|
||||
selector = `[data-fieldname="${fieldname}"] .ace_text-input`;
|
||||
}
|
||||
|
||||
cy.get(selector).as('input');
|
||||
|
|
@ -46,7 +50,7 @@ Cypress.Commands.add('fill_field', (fieldname, value, fieldtype='Data') => {
|
|||
if (fieldtype === 'Select') {
|
||||
return cy.get('@input').select(value);
|
||||
} else {
|
||||
return cy.get('@input').type(value);
|
||||
return cy.get('@input').type(value, {waitForAnimations: false});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -75,3 +79,12 @@ Cypress.Commands.add('dialog', (title, fields) => {
|
|||
return d;
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('get_open_dialog', () => {
|
||||
return cy.get('.modal:visible').last();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('hide_dialog', () => {
|
||||
cy.get_open_dialog().find('.btn-modal-close').click();
|
||||
cy.get('.modal:visible').should('not.exist');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ from faker import Faker
|
|||
# public
|
||||
from .exceptions import *
|
||||
from .utils.jinja import (get_jenv, get_template, render_template, get_email_from_template, get_jloader)
|
||||
from .utils.error import get_frame_locals
|
||||
|
||||
# Hamless for Python 3
|
||||
# For Python 2 set default encoding to utf-8
|
||||
|
|
@ -188,15 +187,20 @@ def connect(site=None, db_name=None):
|
|||
local.db = get_db(user=db_name or local.conf.db_name)
|
||||
set_user("Administrator")
|
||||
|
||||
def connect_read_only():
|
||||
def connect_replica():
|
||||
from frappe.database import get_db
|
||||
user = local.conf.db_name
|
||||
password = local.conf.db_password
|
||||
|
||||
local.read_only_db = get_db(local.conf.slave_host, local.conf.slave_db_name,
|
||||
local.conf.slave_db_password)
|
||||
if local.conf.different_credentials_for_replica:
|
||||
user = local.conf.replica_db_name
|
||||
password = local.conf.replica_db_password
|
||||
|
||||
local.replica_db = get_db(host=local.conf.replica_host, user=user, password=password)
|
||||
|
||||
# swap db connections
|
||||
local.master_db = local.db
|
||||
local.db = local.read_only_db
|
||||
local.primary_db = local.db
|
||||
local.db = local.replica_db
|
||||
|
||||
def get_site_config(sites_path=None, site_path=None):
|
||||
"""Returns `site_config.json` combined with `sites/common_site_config.json`.
|
||||
|
|
@ -274,7 +278,7 @@ def errprint(msg):
|
|||
if not request or (not "cmd" in local.form_dict) or conf.developer_mode:
|
||||
print(msg)
|
||||
|
||||
error_log.append({"exc": msg, "locals": get_frame_locals()})
|
||||
error_log.append({"exc": msg})
|
||||
|
||||
def log(msg):
|
||||
"""Add to `debug_log`.
|
||||
|
|
@ -496,16 +500,17 @@ def whitelist(allow_guest=False, xss_safe=False):
|
|||
def read_only():
|
||||
def innfn(fn):
|
||||
def wrapper_fn(*args, **kwargs):
|
||||
if conf.use_slave_for_read_only:
|
||||
connect_read_only()
|
||||
if conf.read_from_replica:
|
||||
connect_replica()
|
||||
|
||||
try:
|
||||
retval = fn(*args, **get_newargs(fn, kwargs))
|
||||
except:
|
||||
raise
|
||||
finally:
|
||||
if local and hasattr(local, 'master_db'):
|
||||
if local and hasattr(local, 'primary_db'):
|
||||
local.db.close()
|
||||
local.db = local.master_db
|
||||
local.db = local.primary_db
|
||||
|
||||
return retval
|
||||
return wrapper_fn
|
||||
|
|
@ -1262,7 +1267,7 @@ def get_all(doctype, *args, **kwargs):
|
|||
:param fields: List of fields or `*`. Default is: `["name"]`.
|
||||
:param filters: List of filters (see example).
|
||||
:param order_by: Order By e.g. `modified desc`.
|
||||
:param limit_page_start: Start results at record #. Default 0.
|
||||
:param limit_start: Start results at record #. Default 0.
|
||||
:param limit_page_length: No of records in the page. Default 20.
|
||||
|
||||
Example usage:
|
||||
|
|
@ -1297,7 +1302,7 @@ def get_value(*args, **kwargs):
|
|||
|
||||
def as_json(obj, indent=1):
|
||||
from frappe.utils.response import json_handler
|
||||
return json.dumps(obj, indent=indent, sort_keys=True, default=json_handler)
|
||||
return json.dumps(obj, indent=indent, sort_keys=True, default=json_handler, separators=(',', ': '))
|
||||
|
||||
def are_emails_muted():
|
||||
from frappe.utils import cint
|
||||
|
|
@ -1329,14 +1334,15 @@ def format(*args, **kwargs):
|
|||
import frappe.utils.formatters
|
||||
return frappe.utils.formatters.format_value(*args, **kwargs)
|
||||
|
||||
def get_print(doctype=None, name=None, print_format=None, style=None, html=None, as_pdf=False, doc=None, output = None, no_letterhead = 0):
|
||||
def get_print(doctype=None, name=None, print_format=None, style=None, html=None, as_pdf=False, doc=None, output = None, no_letterhead = 0, password=None):
|
||||
"""Get Print Format for given document.
|
||||
|
||||
:param doctype: DocType of document.
|
||||
:param name: Name of document.
|
||||
:param print_format: Print Format name. Default 'Standard',
|
||||
:param style: Print Format style.
|
||||
:param as_pdf: Return as PDF. Default False."""
|
||||
:param as_pdf: Return as PDF. Default False.
|
||||
:param password: Password to encrypt the pdf with. Default None"""
|
||||
from frappe.website.render import build_page
|
||||
from frappe.utils.pdf import get_pdf
|
||||
|
||||
|
|
@ -1347,15 +1353,19 @@ def get_print(doctype=None, name=None, print_format=None, style=None, html=None,
|
|||
local.form_dict.doc = doc
|
||||
local.form_dict.no_letterhead = no_letterhead
|
||||
|
||||
options = None
|
||||
if password:
|
||||
options = {'password': password}
|
||||
|
||||
if not html:
|
||||
html = build_page("printview")
|
||||
|
||||
if as_pdf:
|
||||
return get_pdf(html, output = output)
|
||||
return get_pdf(html, output = output, options = options)
|
||||
else:
|
||||
return html
|
||||
|
||||
def attach_print(doctype, name, file_name=None, print_format=None, style=None, html=None, doc=None, lang=None, print_letterhead=True):
|
||||
def attach_print(doctype, name, file_name=None, print_format=None, style=None, html=None, doc=None, lang=None, print_letterhead=True, password=None):
|
||||
from frappe.utils import scrub_urls
|
||||
|
||||
if not file_name: file_name = name
|
||||
|
|
@ -1374,12 +1384,12 @@ def attach_print(doctype, name, file_name=None, print_format=None, style=None, h
|
|||
if int(print_settings.send_print_as_pdf or 0):
|
||||
out = {
|
||||
"fname": file_name + ".pdf",
|
||||
"fcontent": get_print(doctype, name, print_format=print_format, style=style, html=html, as_pdf=True, doc=doc, no_letterhead=no_letterhead)
|
||||
"fcontent": get_print(doctype, name, print_format=print_format, style=style, html=html, as_pdf=True, doc=doc, no_letterhead=no_letterhead, password=password)
|
||||
}
|
||||
else:
|
||||
out = {
|
||||
"fname": file_name + ".html",
|
||||
"fcontent": scrub_urls(get_print(doctype, name, print_format=print_format, style=style, html=html, doc=doc, no_letterhead=no_letterhead)).encode("utf-8")
|
||||
"fcontent": scrub_urls(get_print(doctype, name, print_format=print_format, style=style, html=html, doc=doc, no_letterhead=no_letterhead, password=password)).encode("utf-8")
|
||||
}
|
||||
|
||||
local.flags.ignore_print_permissions = False
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "document_type",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
|
|
@ -56,6 +57,7 @@
|
|||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "Higher priority rule will be applied first",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "priority",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 0,
|
||||
|
|
@ -88,6 +90,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "disabled",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
|
|
@ -120,6 +123,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
|
|
@ -153,6 +157,7 @@
|
|||
"columns": 0,
|
||||
"default": "Automatic Assignment",
|
||||
"description": "Example: {{ subject }}",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
|
|
@ -185,6 +190,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "assignment_rules_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
|
|
@ -218,8 +224,9 @@
|
|||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "Simple Python Expression, Example: status == 'Open' and type == 'Bug'",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "assign_condition",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Code",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
|
|
@ -250,6 +257,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_6",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
|
|
@ -282,8 +290,9 @@
|
|||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "Simple Python Expression, Example: Status in (\"Closed\", \"Cancelled\")",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "unassign_condition",
|
||||
"fieldtype": "Small Text",
|
||||
"fieldtype": "Code",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
|
|
@ -314,6 +323,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "assign_to_users_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
|
|
@ -346,6 +356,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "rule",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
|
|
@ -379,6 +390,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "users",
|
||||
"fieldtype": "Table MultiSelect",
|
||||
"hidden": 0,
|
||||
|
|
@ -412,6 +424,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "last_user",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
|
|
@ -440,16 +453,14 @@
|
|||
}
|
||||
],
|
||||
"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-03-08 15:13:01.379471",
|
||||
"modified": "2019-04-16 17:46:04.890120",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Automation",
|
||||
"name": "Assignment Rule",
|
||||
|
|
@ -478,7 +489,6 @@
|
|||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
|
|
|
|||
|
|
@ -7,13 +7,14 @@ from __future__ import unicode_literals
|
|||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe.desk.form import assign_to
|
||||
import frappe.cache_manager
|
||||
|
||||
class AssignmentRule(Document):
|
||||
def on_update(self): # pylint: disable=no-self-use
|
||||
frappe.cache().delete_value('assignment_rule')
|
||||
frappe.cache_manager.clear_doctype_map('Assignment Rule', self.name)
|
||||
|
||||
def after_rename(self): # pylint: disable=no-self-use
|
||||
frappe.cache().delete_value('assignment_rule')
|
||||
frappe.cache_manager.clear_doctype_map('Assignment Rule', self.name)
|
||||
|
||||
def apply_unassign(self, doc, assignments):
|
||||
if (self.unassign_condition and
|
||||
|
|
@ -113,14 +114,14 @@ def apply(doc, method):
|
|||
if frappe.flags.in_patch or frappe.flags.in_install:
|
||||
return
|
||||
|
||||
assignment_rules = frappe.cache().get_value('assignment_rule', get_assignment_rules)
|
||||
assignment_rules = frappe.cache_manager.get_doctype_map('Assignment Rule', doc.doctype, dict(
|
||||
document_type = doc.doctype, disabled = 0), order_by = 'priority desc')
|
||||
|
||||
assignment_rule_docs = []
|
||||
|
||||
# build rules
|
||||
if doc.doctype in assignment_rules:
|
||||
# multiple auto assigns
|
||||
for d in frappe.db.get_all('Assignment Rule', dict(document_type=doc.doctype, disabled = 0), order_by = 'priority desc'):
|
||||
assignment_rule_docs.append(frappe.get_doc('Assignment Rule', d.name))
|
||||
# multiple auto assigns
|
||||
for d in assignment_rules:
|
||||
assignment_rule_docs.append(frappe.get_doc('Assignment Rule', d.name))
|
||||
|
||||
if not assignment_rule_docs:
|
||||
return
|
||||
|
|
|
|||
8
frappe/automation/doctype/milestone/milestone.js
Normal file
8
frappe/automation/doctype/milestone/milestone.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) 2019, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Milestone', {
|
||||
// refresh: function(frm) {
|
||||
|
||||
// }
|
||||
});
|
||||
230
frappe/automation/doctype/milestone/milestone.json
Normal file
230
frappe/automation/doctype/milestone/milestone.json
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "",
|
||||
"beta": 0,
|
||||
"creation": "2019-04-17 09:39:15.647817",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 0,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "reference_type",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Document Type",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 1,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "reference_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Document",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "track_field",
|
||||
"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": "Track Field",
|
||||
"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": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "value",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Value",
|
||||
"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": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "milestone_tracker",
|
||||
"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": "Milestone Tracker",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Milestone Tracker",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"in_create": 1,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-04-17 16:01:21.430344",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Automation",
|
||||
"name": "Milestone",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC",
|
||||
"title_field": "reference_type",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
||||
14
frappe/automation/doctype/milestone/milestone.py
Normal file
14
frappe/automation/doctype/milestone/milestone.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2019, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class Milestone(Document):
|
||||
pass
|
||||
|
||||
def on_doctype_update():
|
||||
frappe.db.add_index("Milestone", ["reference_type", "reference_name"])
|
||||
10
frappe/automation/doctype/milestone/test_milestone.py
Normal file
10
frappe/automation/doctype/milestone/test_milestone.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2019, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
#import frappe
|
||||
import unittest
|
||||
|
||||
class TestMilestone(unittest.TestCase):
|
||||
pass
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
// Copyright (c) 2019, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Milestone Tracker', {
|
||||
refresh: function(frm) {
|
||||
frm.trigger('update_options');
|
||||
},
|
||||
document_type: function(frm) {
|
||||
frm.trigger('update_options');
|
||||
},
|
||||
update_options: function(frm) {
|
||||
// update select options for `track_field`
|
||||
let doctype = frm.doc.document_type;
|
||||
let track_fields = [];
|
||||
|
||||
if (doctype) {
|
||||
frappe.model.with_doctype(doctype, () => {
|
||||
// get all date and datetime fields
|
||||
frappe.get_meta(doctype).fields.map(df => {
|
||||
if (['Link', 'Select'].includes(df.fieldtype)) {
|
||||
track_fields.push({label: df.label, value: df.fieldname});
|
||||
}
|
||||
});
|
||||
frm.set_df_property('track_field', 'options', track_fields);
|
||||
});
|
||||
} else {
|
||||
// update select options
|
||||
frm.set_df_property('track_field', 'options', []);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "format:{document_type}-{track_field}",
|
||||
"beta": 0,
|
||||
"creation": "2019-04-17 09:36:41.774774",
|
||||
"custom": 0,
|
||||
"description": "Track milestones for any document",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 0,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "document_type",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Document Type to Track",
|
||||
"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": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "track_field",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Field to Track",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "disabled",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Disabled",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-04-22 16:03:32.848937",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Automation",
|
||||
"name": "Milestone Tracker",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2019, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
import frappe.cache_manager
|
||||
|
||||
class MilestoneTracker(Document):
|
||||
def on_update(self):
|
||||
frappe.cache_manager.clear_doctype_map('Milestone Tracker', self.name)
|
||||
|
||||
def on_trash(self):
|
||||
frappe.cache_manager.clear_doctype_map('Milestone Tracker', self.name)
|
||||
|
||||
def apply(self, doc):
|
||||
before_save = doc.get_doc_before_save()
|
||||
from_value = before_save and before_save.get(self.track_field) or None
|
||||
if from_value != doc.get(self.track_field):
|
||||
frappe.get_doc(dict(
|
||||
doctype = 'Milestone',
|
||||
reference_type = doc.doctype,
|
||||
reference_name = doc.name,
|
||||
track_field = self.track_field,
|
||||
from_value = from_value,
|
||||
value = doc.get(self.track_field),
|
||||
milestone_tracker = self.name,
|
||||
)).insert(ignore_permissions=True)
|
||||
|
||||
def evaluate_milestone(doc, event):
|
||||
for d in frappe.cache_manager.get_doctype_map('Milestone Tracker', doc.doctype,
|
||||
dict(document_type = doc.doctype, disabled=0)):
|
||||
frappe.get_doc('Milestone Tracker', d.name).apply(doc)
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2019, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
class TestMilestoneTracker(unittest.TestCase):
|
||||
def test_milestone(self):
|
||||
frappe.db.sql('delete from `tabMilestone Tracker`')
|
||||
frappe.get_doc(dict(
|
||||
doctype = 'Milestone Tracker',
|
||||
document_type = 'ToDo',
|
||||
track_field = 'status'
|
||||
)).insert()
|
||||
|
||||
todo = frappe.get_doc(dict(
|
||||
doctype = 'ToDo',
|
||||
description = 'test milestone'
|
||||
)).insert()
|
||||
|
||||
milestones = frappe.get_all('Milestone',
|
||||
fields = ['track_field', 'value', 'milestone_tracker'],
|
||||
filters = dict(reference_type = todo.doctype, reference_name=todo.name))
|
||||
|
||||
self.assertEqual(len(milestones), 1)
|
||||
self.assertEqual(milestones[0].track_field, 'status')
|
||||
self.assertEqual(milestones[0].value, 'Open')
|
||||
|
||||
todo.status = 'Closed'
|
||||
todo.save()
|
||||
|
||||
milestones = frappe.get_all('Milestone',
|
||||
fields = ['track_field', 'value', 'milestone_tracker'],
|
||||
filters = dict(reference_type = todo.doctype, reference_name=todo.name),
|
||||
order_by = 'modified desc')
|
||||
|
||||
self.assertEqual(len(milestones), 2)
|
||||
self.assertEqual(milestones[0].track_field, 'status')
|
||||
self.assertEqual(milestones[0].value, 'Closed')
|
||||
|
||||
|
|
@ -16,8 +16,9 @@ from frappe.desk.form.load import get_meta_bundle
|
|||
from frappe.utils.change_log import get_versions
|
||||
from frappe.translate import get_lang_dict
|
||||
from frappe.email.inbox import get_email_accounts
|
||||
from frappe.core.doctype.feedback_trigger.feedback_trigger import get_enabled_feedback_trigger
|
||||
from frappe.social.doctype.energy_point_settings.energy_point_settings import is_energy_point_enabled
|
||||
from frappe.social.doctype.energy_point_log.energy_point_log import get_energy_points
|
||||
from frappe.social.doctype.post.post import frequently_visited_links
|
||||
|
||||
def get_bootinfo():
|
||||
"""build and return boot info"""
|
||||
|
|
@ -75,11 +76,12 @@ def get_bootinfo():
|
|||
bootinfo.calendars = sorted(frappe.get_hooks("calendars"))
|
||||
bootinfo.treeviews = frappe.get_hooks("treeviews") or []
|
||||
bootinfo.lang_dict = get_lang_dict()
|
||||
bootinfo.feedback_triggers = get_enabled_feedback_trigger()
|
||||
bootinfo.gsuite_enabled = get_gsuite_status()
|
||||
bootinfo.success_action = get_success_action()
|
||||
bootinfo.update(get_email_accounts(user=frappe.session.user))
|
||||
bootinfo.energy_points_enabled = is_energy_point_enabled()
|
||||
bootinfo.points = get_energy_points(frappe.session.user)
|
||||
bootinfo.frequently_visited_links = frequently_visited_links()
|
||||
|
||||
return bootinfo
|
||||
|
||||
|
|
|
|||
|
|
@ -3,31 +3,40 @@
|
|||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import frappe, json
|
||||
import frappe.defaults
|
||||
from frappe.desk.notifications import (delete_notification_count_for,
|
||||
clear_notifications)
|
||||
|
||||
common_default_keys = ["__default", "__global"]
|
||||
|
||||
def clear_user_cache(user=None):
|
||||
cache = frappe.cache()
|
||||
global_cache_keys = ("app_hooks", "installed_apps",
|
||||
"app_modules", "module_app", "notification_config", 'system_settings',
|
||||
'scheduler_events', 'time_zone', 'webhooks', 'active_domains',
|
||||
'active_modules', 'assignment_rule')
|
||||
|
||||
groups = ("bootinfo", "user_recent", "roles", "user_doc", "lang",
|
||||
user_cache_keys = ("bootinfo", "user_recent", "roles", "user_doc", "lang",
|
||||
"defaults", "user_permissions", "home_page", "linked_with",
|
||||
"desktop_icons", 'portal_menu_items')
|
||||
|
||||
doctype_cache_keys = ("meta", "form_meta", "table_columns", "last_modified",
|
||||
"linked_doctypes", 'notifications', 'workflow' ,'energy_point_rule_map')
|
||||
|
||||
|
||||
def clear_user_cache(user=None):
|
||||
cache = frappe.cache()
|
||||
|
||||
# this will automatically reload the global cache
|
||||
# so it is important to clear this first
|
||||
clear_notifications(user)
|
||||
|
||||
if user:
|
||||
for name in groups:
|
||||
for name in user_cache_keys:
|
||||
cache.hdel(name, user)
|
||||
cache.delete_keys("user:" + user)
|
||||
clear_defaults_cache(user)
|
||||
else:
|
||||
for name in groups:
|
||||
for name in user_cache_keys:
|
||||
cache.delete_key(name)
|
||||
clear_defaults_cache()
|
||||
clear_global_cache()
|
||||
|
|
@ -37,10 +46,7 @@ def clear_global_cache():
|
|||
|
||||
clear_doctype_cache()
|
||||
clear_website_cache()
|
||||
frappe.cache().delete_value(["app_hooks", "installed_apps",
|
||||
"app_modules", "module_app", "notification_config", 'system_settings',
|
||||
'scheduler_events', 'time_zone', 'webhooks', 'active_domains',
|
||||
'active_modules', 'assignment_rule'])
|
||||
frappe.cache().delete_value(global_cache_keys)
|
||||
frappe.setup_module_map()
|
||||
|
||||
def clear_defaults_cache(user=None):
|
||||
|
|
@ -63,11 +69,8 @@ def clear_doctype_cache(doctype=None):
|
|||
for key in ('is_table', 'doctype_modules'):
|
||||
cache.delete_value(key)
|
||||
|
||||
groups = ["meta", "form_meta", "table_columns", "last_modified",
|
||||
"linked_doctypes", 'notifications', 'workflow']
|
||||
|
||||
def clear_single(dt):
|
||||
for name in groups:
|
||||
for name in doctype_cache_keys:
|
||||
cache.hdel(name, dt)
|
||||
|
||||
if doctype:
|
||||
|
|
@ -84,9 +87,31 @@ def clear_doctype_cache(doctype=None):
|
|||
|
||||
else:
|
||||
# clear all
|
||||
for name in groups:
|
||||
for name in doctype_cache_keys:
|
||||
cache.delete_value(name)
|
||||
|
||||
# Clear all document's cache. To clear documents of a specific DocType document_cache should be restructured
|
||||
clear_document_cache()
|
||||
|
||||
def get_doctype_map(doctype, name, filters, order_by=None):
|
||||
cache = frappe.cache()
|
||||
cache_key = frappe.scrub(doctype) + '_map'
|
||||
doctype_map = cache.hget(cache_key, name)
|
||||
|
||||
if doctype_map:
|
||||
# cached, return
|
||||
items = json.loads(doctype_map)
|
||||
else:
|
||||
# non cached, build cache
|
||||
try:
|
||||
items = frappe.get_all(doctype, filters=filters, order_by = order_by)
|
||||
cache.hset(cache_key, doctype, json.dumps(items))
|
||||
except frappe.db.TableMissingError:
|
||||
# executed from inside patch, ignore
|
||||
items = []
|
||||
|
||||
return items
|
||||
|
||||
def clear_doctype_map(doctype, name):
|
||||
cache_key = frappe.scrub(doctype) + '_map'
|
||||
frappe.cache().hdel(cache_key, name)
|
||||
|
|
@ -17,10 +17,6 @@ from frappe.chat.util import (
|
|||
session = frappe.session
|
||||
|
||||
class ChatProfile(Document):
|
||||
def before_save(self):
|
||||
if not self.is_new():
|
||||
self.get_doc_before_save()
|
||||
|
||||
def on_update(self):
|
||||
if not self.is_new():
|
||||
b, a = self.get_doc_before_save(), self
|
||||
|
|
|
|||
|
|
@ -69,10 +69,6 @@ class ChatRoom(Document):
|
|||
if self.type == "Group" and not self.room_name:
|
||||
frappe.throw(_('Group name cannot be empty.'))
|
||||
|
||||
def before_save(self):
|
||||
if not self.is_new():
|
||||
self.get_doc_before_save()
|
||||
|
||||
def on_update(self):
|
||||
if not self.is_new():
|
||||
before = self.get_doc_before_save()
|
||||
|
|
|
|||
|
|
@ -371,4 +371,5 @@ def check_parent_permission(parent, child_doctype):
|
|||
if frappe.permissions.has_permission(parent):
|
||||
return
|
||||
# Either parent not passed or the user doesn't have permission on parent doctype of child table!
|
||||
raise frappe.PermissionError
|
||||
raise frappe.PermissionError
|
||||
|
||||
|
|
|
|||
|
|
@ -157,16 +157,17 @@ def _reinstall(site, admin_password=None, mariadb_root_username=None, mariadb_ro
|
|||
admin_password=admin_password)
|
||||
|
||||
@click.command('install-app')
|
||||
@click.argument('app')
|
||||
@click.argument('apps', nargs=-1)
|
||||
@pass_context
|
||||
def install_app(context, app):
|
||||
"Install a new app to site"
|
||||
def install_app(context, apps):
|
||||
"Install a new app to site, supports multiple apps"
|
||||
from frappe.installer import install_app as _install_app
|
||||
for site in context.sites:
|
||||
frappe.init(site=site)
|
||||
frappe.connect()
|
||||
try:
|
||||
_install_app(app, verbose=context.verbose)
|
||||
for app in apps:
|
||||
_install_app(app, verbose=context.verbose)
|
||||
finally:
|
||||
frappe.destroy()
|
||||
|
||||
|
|
|
|||
|
|
@ -625,19 +625,30 @@ def setup_help(context):
|
|||
print_in_app_help_deprecation()
|
||||
|
||||
@click.command('rebuild-global-search')
|
||||
@click.option('--static-pages', is_flag=True, default=False, help='Rebuild global search for static pages')
|
||||
@pass_context
|
||||
def rebuild_global_search(context):
|
||||
def rebuild_global_search(context, static_pages=False):
|
||||
'''Setup help table in the current site (called after migrate)'''
|
||||
from frappe.utils.global_search import (get_doctypes_with_global_search, rebuild_for_doctype)
|
||||
from frappe.utils.global_search import (get_doctypes_with_global_search, rebuild_for_doctype,
|
||||
get_routes_to_index, add_route_to_global_search, sync_global_search)
|
||||
|
||||
for site in context.sites:
|
||||
try:
|
||||
frappe.init(site)
|
||||
frappe.connect()
|
||||
doctypes = get_doctypes_with_global_search()
|
||||
for i, doctype in enumerate(doctypes):
|
||||
rebuild_for_doctype(doctype)
|
||||
update_progress_bar('Rebuilding Global Search', i, len(doctypes))
|
||||
|
||||
if static_pages:
|
||||
routes = get_routes_to_index()
|
||||
for i, route in enumerate(routes):
|
||||
add_route_to_global_search(route)
|
||||
frappe.local.request = None
|
||||
update_progress_bar('Rebuilding Global Search', i, len(routes))
|
||||
sync_global_search()
|
||||
else:
|
||||
doctypes = get_doctypes_with_global_search()
|
||||
for i, doctype in enumerate(doctypes):
|
||||
rebuild_for_doctype(doctype)
|
||||
update_progress_bar('Rebuilding Global Search', i, len(doctypes))
|
||||
|
||||
finally:
|
||||
frappe.destroy()
|
||||
|
|
|
|||
|
|
@ -78,15 +78,22 @@ def get_modules_from_app(app):
|
|||
return active_modules_list
|
||||
|
||||
def get_all_empty_tables_by_module():
|
||||
results = frappe.db.sql("""
|
||||
SELECT
|
||||
name, module
|
||||
FROM information_schema.tables as i
|
||||
JOIN tabDocType as d
|
||||
ON i.table_name = CONCAT('tab', d.name)
|
||||
WHERE table_rows = 0;
|
||||
|
||||
""")
|
||||
results = frappe.db.multisql({
|
||||
'mariadb': '''
|
||||
SELECT `name`, `module`
|
||||
FROM information_schema.tables AS i
|
||||
JOIN `tabDocType` AS d
|
||||
ON i.table_name = CONCAT('tab', d.name)
|
||||
WHERE `table_rows` = 0;
|
||||
''',
|
||||
'postgres': '''
|
||||
SELECT "name", "module"
|
||||
FROM "pg_stat_all_tables" AS i
|
||||
JOIN "tabDocType" AS d
|
||||
ON i.relname = CONCAT('tab', d.name)
|
||||
WHERE n_tup_ins = 0;
|
||||
'''
|
||||
})
|
||||
|
||||
empty_tables_by_module = {}
|
||||
|
||||
|
|
@ -95,7 +102,6 @@ def get_all_empty_tables_by_module():
|
|||
empty_tables_by_module[module].append(doctype)
|
||||
else:
|
||||
empty_tables_by_module[module] = [doctype]
|
||||
|
||||
return empty_tables_by_module
|
||||
|
||||
def is_domain(module):
|
||||
|
|
|
|||
|
|
@ -152,3 +152,11 @@ def filter_dynamic_link_doctypes(doctype, txt, searchfield, start, page_len, fil
|
|||
valid_doctypes = [[doctype] for doctype in valid_doctypes]
|
||||
|
||||
return valid_doctypes
|
||||
|
||||
def set_link_title(doc):
|
||||
if not doc.links:
|
||||
return
|
||||
for link in doc.links:
|
||||
if not link.link_title:
|
||||
linked_doc = frappe.get_doc(link.link_doctype, link.link_name)
|
||||
link.link_title = linked_doc.get("title_field") or linked_doc.get("name")
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from frappe.model.naming import make_autoname
|
|||
from frappe.core.doctype.dynamic_link.dynamic_link import deduplicate_dynamic_links
|
||||
from six import iteritems, string_types
|
||||
from past.builtins import cmp
|
||||
from frappe.contacts.address_and_contact import set_link_title
|
||||
|
||||
import functools
|
||||
|
||||
|
|
@ -39,6 +40,7 @@ class Address(Document):
|
|||
def validate(self):
|
||||
self.link_address()
|
||||
self.validate_reference()
|
||||
set_link_title(self)
|
||||
deduplicate_dynamic_links(self)
|
||||
|
||||
def link_address(self):
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from frappe.core.doctype.dynamic_link.dynamic_link import deduplicate_dynamic_li
|
|||
from six import iteritems
|
||||
from past.builtins import cmp
|
||||
from frappe.model.naming import append_number_if_name_exists
|
||||
from frappe.contacts.address_and_contact import set_link_title
|
||||
|
||||
import functools
|
||||
|
||||
|
|
@ -31,6 +32,7 @@ class Contact(Document):
|
|||
if self.email_id:
|
||||
self.email_id = self.email_id.strip()
|
||||
self.set_user()
|
||||
set_link_title(self)
|
||||
if self.email_id and not self.image:
|
||||
self.image = has_gravatar(self.email_id)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ from __future__ import unicode_literals
|
|||
from frappe import _
|
||||
from frappe.utils import get_fullname, now
|
||||
from frappe.model.document import Document
|
||||
from frappe.core.utils import get_parent_doc, set_timeline_doc
|
||||
from frappe.core.utils import set_timeline_doc
|
||||
import frappe
|
||||
|
||||
class ActivityLog(Document):
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ def get_feed_match_conditions(user=None, doctype='Comment'):
|
|||
user_permissions = frappe.permissions.get_user_permissions(user)
|
||||
can_read = frappe.get_user().get_can_read()
|
||||
|
||||
can_read_doctypes = ['"{}"'.format(doctype) for doctype in
|
||||
can_read_doctypes = ["'{}'".format(doctype) for doctype in
|
||||
list(set(can_read) - set(list(user_permissions)))]
|
||||
|
||||
if can_read_doctypes:
|
||||
|
|
|
|||
|
|
@ -48,10 +48,10 @@ class TestComment(unittest.TestCase):
|
|||
add_comment('pleez vizits my site http://mysite.com', 'test@test.com', 'bad commentor',
|
||||
'Blog Post', test_blog.name, test_blog.route)
|
||||
|
||||
self.assertEqual(frappe.get_all('Comment', fields = ['*'], filters = dict(
|
||||
self.assertEqual(len(frappe.get_all('Comment', fields = ['*'], filters = dict(
|
||||
reference_doctype = test_blog.doctype,
|
||||
reference_name = test_blog.name
|
||||
))[0].published, 0)
|
||||
))), 0)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -31,13 +31,6 @@ frappe.ui.form.on("Communication", {
|
|||
}
|
||||
}
|
||||
|
||||
if(frm.doc.communication_type == "Feedback") {
|
||||
frm.add_custom_button(__("Resend"), function() {
|
||||
var feedback = new frappe.utils.Feedback();
|
||||
feedback.resend_feedback_request(frm.doc);
|
||||
});
|
||||
}
|
||||
|
||||
if(frm.doc.status==="Open") {
|
||||
frm.add_custom_button(__("Close"), function() {
|
||||
frm.set_value("status", "Closed");
|
||||
|
|
@ -54,7 +47,7 @@ frappe.ui.form.on("Communication", {
|
|||
frm.trigger('show_relink_dialog');
|
||||
});
|
||||
|
||||
if(frm.doc.communication_type=="Communication"
|
||||
if(frm.doc.communication_type=="Communication"
|
||||
&& frm.doc.communication_medium == "Email"
|
||||
&& frm.doc.sent_or_received == "Received") {
|
||||
|
||||
|
|
@ -90,7 +83,7 @@ frappe.ui.form.on("Communication", {
|
|||
}
|
||||
}
|
||||
|
||||
if(frm.doc.communication_type=="Communication"
|
||||
if(frm.doc.communication_type=="Communication"
|
||||
&& frm.doc.communication_medium == "Phone"
|
||||
&& frm.doc.sent_or_received == "Received"){
|
||||
|
||||
|
|
@ -185,7 +178,7 @@ frappe.ui.form.on("Communication", {
|
|||
|
||||
forward_mail: function(frm) {
|
||||
var args = frm.events.get_mail_args(frm)
|
||||
$.extend(args, {
|
||||
$.extend(args, {
|
||||
forward: true,
|
||||
subject: __("Fw: {0}", [frm.doc.subject]),
|
||||
})
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -8,11 +8,11 @@ from frappe.model.document import Document
|
|||
from frappe.utils import validate_email_address, get_fullname, strip_html, cstr
|
||||
from frappe.core.doctype.communication.email import (validate_email,
|
||||
notify, _notify, update_parent_mins_to_first_response)
|
||||
from frappe.core.utils import get_parent_doc, set_timeline_doc
|
||||
from frappe.core.utils import get_parent_doc
|
||||
from frappe.utils.bot import BotReply
|
||||
from frappe.utils import parse_addr
|
||||
from frappe.core.doctype.comment.comment import update_comment_in_doc
|
||||
|
||||
from email.utils import parseaddr
|
||||
from collections import Counter
|
||||
|
||||
exclude_from_linked_with = True
|
||||
|
|
@ -58,7 +58,10 @@ class Communication(Document):
|
|||
self.set_sender_full_name()
|
||||
|
||||
validate_email(self)
|
||||
set_timeline_doc(self)
|
||||
|
||||
if self.communication_medium == "Email":
|
||||
self.set_timeline_links()
|
||||
self.deduplicate_timeline_links()
|
||||
|
||||
def validate_reference(self):
|
||||
if self.reference_doctype and self.reference_name:
|
||||
|
|
@ -79,6 +82,7 @@ class Communication(Document):
|
|||
circular_linking = True
|
||||
break
|
||||
doc = get_parent_doc(doc)
|
||||
|
||||
if circular_linking:
|
||||
frappe.throw(_("Please make sure the Reference Communication Docs are not circularly linked."), frappe.CircularLinkingError)
|
||||
|
||||
|
|
@ -231,26 +235,66 @@ class Communication(Document):
|
|||
if commit:
|
||||
frappe.db.commit()
|
||||
|
||||
# Timeline Links
|
||||
def set_timeline_links(self):
|
||||
contacts = get_contacts([self.sender, self.recipients, self.cc, self.bcc])
|
||||
for contact_name in contacts:
|
||||
self.add_link('Contact', contact_name)
|
||||
|
||||
#link contact's dynamic links to communication
|
||||
add_contact_links_to_communication(self, contact_name)
|
||||
|
||||
def deduplicate_timeline_links(self):
|
||||
if self.timeline_links:
|
||||
links, duplicate = [], False
|
||||
|
||||
for l in self.timeline_links:
|
||||
t = (l.link_doctype, l.link_name)
|
||||
if not t in links:
|
||||
links.append(t)
|
||||
else:
|
||||
duplicate = True
|
||||
|
||||
if duplicate:
|
||||
del self.timeline_links[:] # make it python 2 compatible as list.clear() is python 3 only
|
||||
for l in links:
|
||||
self.add_link(link_doctype=l[0], link_name=l[1])
|
||||
|
||||
def add_link(self, link_doctype, link_name, autosave=False):
|
||||
self.append("timeline_links",
|
||||
{
|
||||
"link_doctype": link_doctype,
|
||||
"link_name": link_name
|
||||
}
|
||||
)
|
||||
|
||||
if autosave:
|
||||
self.save(ignore_permissions=True)
|
||||
|
||||
def get_links(self):
|
||||
return self.timeline_links
|
||||
|
||||
def remove_link(self, link_doctype, link_name, autosave=False, ignore_permissions=True):
|
||||
for l in self.timeline_links:
|
||||
if l.link_doctype == link_doctype and l.link_name == link_name:
|
||||
self.timeline_links.remove(l)
|
||||
|
||||
if autosave:
|
||||
self.save(ignore_permissions=ignore_permissions)
|
||||
|
||||
def on_doctype_update():
|
||||
"""Add indexes in `tabCommunication`"""
|
||||
frappe.db.add_index("Communication", ["reference_doctype", "reference_name"])
|
||||
frappe.db.add_index("Communication", ["timeline_doctype", "timeline_name"])
|
||||
frappe.db.add_index("Communication", ["link_doctype", "link_name"])
|
||||
frappe.db.add_index("Communication", ["status", "communication_type"])
|
||||
|
||||
def has_permission(doc, ptype, user):
|
||||
if ptype=="read":
|
||||
if (doc.reference_doctype == "Communication" and doc.reference_name == doc.name) \
|
||||
or (doc.timeline_doctype == "Communication" and doc.timeline_name == doc.name):
|
||||
return
|
||||
if doc.reference_doctype == "Communication" and doc.reference_name == doc.name:
|
||||
return
|
||||
|
||||
if doc.reference_doctype and doc.reference_name:
|
||||
if frappe.has_permission(doc.reference_doctype, ptype="read", doc=doc.reference_name):
|
||||
return True
|
||||
if doc.timeline_doctype and doc.timeline_name:
|
||||
if frappe.has_permission(doc.timeline_doctype, ptype="read", doc=doc.timeline_name):
|
||||
return True
|
||||
|
||||
def get_permission_query_conditions_for_communication(user):
|
||||
if not user: user = frappe.session.user
|
||||
|
|
@ -265,8 +309,44 @@ def get_permission_query_conditions_for_communication(user):
|
|||
distinct=True, order_by="idx")
|
||||
|
||||
if not accounts:
|
||||
return """tabCommunication.communication_medium!='Email'"""
|
||||
return """`tabCommunication`.communication_medium!='Email'"""
|
||||
|
||||
email_accounts = [ '"%s"'%account.get("email_account") for account in accounts ]
|
||||
return """tabCommunication.email_account in ({email_accounts})"""\
|
||||
return """`tabCommunication`.email_account in ({email_accounts})"""\
|
||||
.format(email_accounts=','.join(email_accounts))
|
||||
|
||||
def get_contacts(email_strings):
|
||||
email_addrs = []
|
||||
|
||||
for email_string in email_strings:
|
||||
if email_string:
|
||||
for email in email_string.split(","):
|
||||
parsed_email = parseaddr(email)[1]
|
||||
if parsed_email:
|
||||
email_addrs.append(parsed_email)
|
||||
|
||||
contacts = []
|
||||
for email in email_addrs:
|
||||
contact_name = frappe.db.get_value('Contact', {'email_id': email})
|
||||
|
||||
if not contact_name:
|
||||
contact = frappe.get_doc({
|
||||
"doctype": "Contact",
|
||||
"first_name": frappe.unscrub(email.split("@")[0]),
|
||||
"email_id": email
|
||||
}).insert(ignore_permissions=True)
|
||||
contact_name = contact.name
|
||||
|
||||
contacts.append(contact_name)
|
||||
|
||||
return contacts
|
||||
|
||||
def add_contact_links_to_communication(communication, contact_name):
|
||||
contact_links = frappe.get_list("Dynamic Link", filters={
|
||||
"parenttype": "Contact",
|
||||
"parent": contact_name
|
||||
}, fields=["link_doctype", "link_name"])
|
||||
|
||||
if contact_links:
|
||||
for contact_link in contact_links:
|
||||
communication.add_link(contact_link.link_doctype, contact_link.link_name)
|
||||
|
|
@ -22,7 +22,7 @@ from frappe.utils.background_jobs import enqueue
|
|||
def make(doctype=None, name=None, content=None, subject=None, sent_or_received = "Sent",
|
||||
sender=None, sender_full_name=None, recipients=None, communication_medium="Email", send_email=False,
|
||||
print_html=None, print_format=None, attachments='[]', send_me_a_copy=False, cc=None, bcc=None,
|
||||
flags=None, read_receipt=None, print_letterhead=True):
|
||||
flags=None, read_receipt=None, print_letterhead=True, email_template=None):
|
||||
"""Make a new communication.
|
||||
|
||||
:param doctype: Reference DocType.
|
||||
|
|
@ -38,6 +38,7 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received =
|
|||
:param print_format: Print Format name of parent document to be sent as attachment.
|
||||
:param attachments: List of attachments as list of files or JSON string.
|
||||
:param send_me_a_copy: Send a copy to the sender (default **False**).
|
||||
:param email_template: Template which is used to compose mail .
|
||||
"""
|
||||
|
||||
is_error_report = (doctype=="User" and name==frappe.session.user and subject=="Error Report")
|
||||
|
|
@ -66,15 +67,13 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received =
|
|||
"sent_or_received": sent_or_received,
|
||||
"reference_doctype": doctype,
|
||||
"reference_name": name,
|
||||
"email_template": email_template,
|
||||
"message_id":get_message_id().strip(" <>"),
|
||||
"read_receipt":read_receipt,
|
||||
"has_attachment": 1 if attachments else 0
|
||||
})
|
||||
comm.insert(ignore_permissions=True)
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
if not doctype:
|
||||
# if no reference given, then send it against the communication
|
||||
comm.db_set(dict(reference_doctype='Communication', reference_name=comm.name))
|
||||
comm.save(ignore_permissions=True)
|
||||
|
||||
if isinstance(attachments, string_types):
|
||||
attachments = json.loads(attachments)
|
||||
|
|
@ -555,5 +554,4 @@ def mark_email_as_seen(name=None):
|
|||
|
||||
frappe.response["type"] = 'binary'
|
||||
frappe.response["filename"] = "imaginary_pixel.png"
|
||||
frappe.response["filecontent"] = buffered_obj.getvalue()
|
||||
|
||||
frappe.response["filecontent"] = buffered_obj.getvalue()
|
||||
|
|
@ -44,28 +44,126 @@ class TestCommunication(unittest.TestCase):
|
|||
self.assertFalse(frappe.utils.parse_addr(x)[0])
|
||||
|
||||
def test_circular_linking(self):
|
||||
content = "This was created to test circular linking"
|
||||
a = frappe.get_doc({
|
||||
"doctype": "Communication",
|
||||
"communication_type": "Communication",
|
||||
"content": content,
|
||||
}).insert()
|
||||
"content": "This was created to test circular linking: Communication A",
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
b = frappe.get_doc({
|
||||
"doctype": "Communication",
|
||||
"communication_type": "Communication",
|
||||
"content": content,
|
||||
"content": "This was created to test circular linking: Communication B",
|
||||
"reference_doctype": "Communication",
|
||||
"reference_name": a.name
|
||||
}).insert()
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
c = frappe.get_doc({
|
||||
"doctype": "Communication",
|
||||
"communication_type": "Communication",
|
||||
"content": content,
|
||||
"content": "This was created to test circular linking: Communication C",
|
||||
"reference_doctype": "Communication",
|
||||
"reference_name": b.name
|
||||
}).insert()
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
a = frappe.get_doc("Communication", a.name)
|
||||
a.reference_doctype = "Communication"
|
||||
a.reference_name = c.name
|
||||
|
||||
self.assertRaises(frappe.CircularLinkingError, a.save)
|
||||
|
||||
def test_deduplication_timeline_links(self):
|
||||
note = frappe.get_doc({
|
||||
"doctype": "Note",
|
||||
"title": "deduplication timeline links",
|
||||
"content": "deduplication timeline links"
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
comm = frappe.get_doc({
|
||||
"doctype": "Communication",
|
||||
"communication_type": "Communication",
|
||||
"content": "Deduplication of Links",
|
||||
"communication_medium": "Email"
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
#adding same link twice
|
||||
comm.add_link(link_doctype="Note", link_name=note.name, autosave=True)
|
||||
comm.add_link(link_doctype="Note", link_name=note.name, autosave=True)
|
||||
|
||||
comm = frappe.get_doc("Communication", comm.name)
|
||||
|
||||
self.assertNotEqual(2, len(comm.timeline_links))
|
||||
|
||||
def test_contacts_attached(self):
|
||||
contact_sender = frappe.get_doc({
|
||||
"doctype": "Contact",
|
||||
"first_name": frappe.generate_hash(length=10),
|
||||
"email_id": "comm_sender@example.com"
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
contact_recipient = frappe.get_doc({
|
||||
"doctype": "Contact",
|
||||
"first_name": frappe.generate_hash(length=10),
|
||||
"email_id": "comm_recipient@example.com"
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
contact_cc = frappe.get_doc({
|
||||
"doctype": "Contact",
|
||||
"first_name": frappe.generate_hash(length=10),
|
||||
"email_id": "comm_cc@example.com"
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
comm = frappe.get_doc({
|
||||
"doctype": "Communication",
|
||||
"communication_medium": "Email",
|
||||
"subject": "Contacts Attached Test",
|
||||
"sender": "comm_sender@example.com",
|
||||
"recipients": "comm_recipient@example.com",
|
||||
"cc": "comm_cc@example.com"
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
comm = frappe.get_doc("Communication", comm.name)
|
||||
|
||||
contact_links = []
|
||||
for timeline_link in comm.timeline_links:
|
||||
contact_links.append(timeline_link.link_name)
|
||||
|
||||
self.assertIn(contact_sender.name, contact_links)
|
||||
self.assertIn(contact_recipient.name, contact_links)
|
||||
self.assertIn(contact_cc.name, contact_links)
|
||||
|
||||
def test_get_communication_data(self):
|
||||
from frappe.desk.form.load import get_communication_data
|
||||
|
||||
note = frappe.get_doc({
|
||||
"doctype": "Note",
|
||||
"title": "get communication data",
|
||||
"content": "get communication data"
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
comm_note_1 = frappe.get_doc({
|
||||
"doctype": "Communication",
|
||||
"communication_type": "Communication",
|
||||
"content": "Test Get Communication Data 1",
|
||||
"communication_medium": "Email"
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
comm_note_1.add_link(link_doctype="Note", link_name=note.name, autosave=True)
|
||||
|
||||
comm_note_2 = frappe.get_doc({
|
||||
"doctype": "Communication",
|
||||
"communication_type": "Communication",
|
||||
"content": "Test Get Communication Data 2",
|
||||
"communication_medium": "Email"
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
comm_note_2.add_link(link_doctype="Note", link_name=note.name, autosave=True)
|
||||
|
||||
comms = get_communication_data("Note", note.name, as_dict=True)
|
||||
|
||||
data = []
|
||||
for comm in comms:
|
||||
data.append(comm.name)
|
||||
|
||||
self.assertIn(comm_note_1.name, data)
|
||||
self.assertIn(comm_note_2.name, data)
|
||||
|
|
@ -30,6 +30,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "",
|
||||
"length": 0,
|
||||
|
|
@ -62,6 +63,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Label",
|
||||
"length": 0,
|
||||
|
|
@ -99,13 +101,14 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Type",
|
||||
"length": 0,
|
||||
"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\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRating\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature",
|
||||
"options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\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,
|
||||
|
|
@ -134,6 +137,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Name",
|
||||
"length": 0,
|
||||
|
|
@ -168,6 +172,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Mandatory",
|
||||
"length": 0,
|
||||
|
|
@ -206,6 +211,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Precision",
|
||||
"length": 0,
|
||||
|
|
@ -240,6 +246,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Length",
|
||||
"length": 0,
|
||||
|
|
@ -273,6 +280,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Index",
|
||||
"length": 0,
|
||||
|
|
@ -309,6 +317,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "In List View",
|
||||
"length": 0,
|
||||
|
|
@ -343,6 +352,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "In Standard Filter",
|
||||
"length": 0,
|
||||
|
|
@ -377,6 +387,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "In Global Search",
|
||||
"length": 0,
|
||||
|
|
@ -394,6 +405,40 @@
|
|||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "in_preview",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "In Preview",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"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,
|
||||
|
|
@ -410,6 +455,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Allow in Quick Entry",
|
||||
"length": 0,
|
||||
|
|
@ -443,6 +489,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Bold",
|
||||
"length": 0,
|
||||
|
|
@ -478,6 +525,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Translatable",
|
||||
"length": 0,
|
||||
|
|
@ -512,6 +560,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Collapsible",
|
||||
"length": 255,
|
||||
|
|
@ -546,6 +595,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Collapsible Depends On",
|
||||
"length": 0,
|
||||
|
|
@ -580,6 +630,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
|
|
@ -612,6 +663,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Options",
|
||||
"length": 0,
|
||||
|
|
@ -646,6 +698,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Default",
|
||||
"length": 0,
|
||||
|
|
@ -680,6 +733,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Fetch From",
|
||||
"length": 0,
|
||||
|
|
@ -714,6 +768,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Fetch If Empty",
|
||||
"length": 0,
|
||||
|
|
@ -747,6 +802,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Permissions",
|
||||
"length": 0,
|
||||
|
|
@ -779,6 +835,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Display Depends On",
|
||||
"length": 255,
|
||||
|
|
@ -814,6 +871,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Hidden",
|
||||
"length": 0,
|
||||
|
|
@ -850,6 +908,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Read Only",
|
||||
"length": 0,
|
||||
|
|
@ -884,6 +943,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Unique",
|
||||
"length": 0,
|
||||
|
|
@ -918,6 +978,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Set Only Once",
|
||||
"length": 0,
|
||||
|
|
@ -951,6 +1012,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Allow Bulk Edit",
|
||||
"length": 0,
|
||||
|
|
@ -984,6 +1046,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
|
|
@ -1016,6 +1079,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Perm Level",
|
||||
"length": 0,
|
||||
|
|
@ -1053,6 +1117,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Ignore User Permissions",
|
||||
"length": 0,
|
||||
|
|
@ -1086,6 +1151,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Allow on Submit",
|
||||
"length": 0,
|
||||
|
|
@ -1122,6 +1188,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Report Hide",
|
||||
"length": 0,
|
||||
|
|
@ -1159,6 +1226,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Remember Last Selected Value",
|
||||
"length": 0,
|
||||
|
|
@ -1193,6 +1261,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Ignore XSS Filter",
|
||||
"length": 0,
|
||||
|
|
@ -1226,6 +1295,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Display",
|
||||
"length": 0,
|
||||
|
|
@ -1258,6 +1328,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "In Filter",
|
||||
"length": 0,
|
||||
|
|
@ -1294,6 +1365,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "No Copy",
|
||||
"length": 0,
|
||||
|
|
@ -1330,6 +1402,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Print Hide",
|
||||
"length": 0,
|
||||
|
|
@ -1367,6 +1440,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Print Hide If No Value",
|
||||
"length": 0,
|
||||
|
|
@ -1400,6 +1474,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Print Width",
|
||||
"length": 0,
|
||||
|
|
@ -1432,6 +1507,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Width",
|
||||
"length": 0,
|
||||
|
|
@ -1470,6 +1546,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Columns",
|
||||
"length": 0,
|
||||
|
|
@ -1503,6 +1580,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
|
|
@ -1534,6 +1612,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Description",
|
||||
"length": 0,
|
||||
|
|
@ -1570,6 +1649,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
|
|
@ -1603,6 +1683,7 @@
|
|||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_preview": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
|
|
@ -1622,16 +1703,14 @@
|
|||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-03-18 17:59:57.873790",
|
||||
"modified": "2019-04-08 12:19:53.415372",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "DocField",
|
||||
|
|
@ -1639,7 +1718,6 @@
|
|||
"permissions": [],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_order": "ASC",
|
||||
"track_changes": 0,
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class Test{classname}(unittest.TestCase):
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -17,7 +17,10 @@ from frappe.desk.notifications import delete_notification_count_for
|
|||
from frappe.modules import make_boilerplate, get_doc_path
|
||||
from frappe.database.schema import validate_column_name, validate_column_length
|
||||
from frappe.model.docfield import supports_translation
|
||||
from frappe.modules.import_file import get_file_path
|
||||
from six import iteritems
|
||||
import frappe.website.render
|
||||
import json
|
||||
|
||||
class InvalidFieldNameError(frappe.ValidationError): pass
|
||||
|
||||
|
|
@ -252,7 +255,8 @@ class DocType(Document):
|
|||
self.update_fields_to_fetch()
|
||||
|
||||
from frappe import conf
|
||||
if not self.custom and not (frappe.flags.in_import or frappe.flags.in_test) and conf.get('developer_mode'):
|
||||
allow_doctype_export = frappe.flags.allow_doctype_export or (not frappe.flags.in_test and conf.get('developer_mode'))
|
||||
if not self.custom and not frappe.flags.in_import and allow_doctype_export:
|
||||
self.export_doc()
|
||||
self.make_controller_template()
|
||||
|
||||
|
|
@ -343,7 +347,8 @@ class DocType(Document):
|
|||
if merge:
|
||||
frappe.throw(_("DocType can not be merged"))
|
||||
|
||||
if not frappe.flags.in_test and not frappe.flags.in_patch:
|
||||
# Do not rename and move files and folders for custom doctype
|
||||
if not self.custom and not frappe.flags.in_test and not frappe.flags.in_patch:
|
||||
self.rename_files_and_folders(old, new)
|
||||
|
||||
def after_rename(self, old, new, merge=False):
|
||||
|
|
@ -403,6 +408,72 @@ class DocType(Document):
|
|||
if naming_series[0].default:
|
||||
make_property_setter(self.name, "naming_series", "default", naming_series[0].default, "Text", validate_fields_for_doctype=False)
|
||||
|
||||
def before_export(self, docdict):
|
||||
# remove null and empty fields
|
||||
def remove_null_fields(o):
|
||||
to_remove = []
|
||||
for attr, value in iteritems(o):
|
||||
if isinstance(value, list):
|
||||
for v in value:
|
||||
remove_null_fields(v)
|
||||
elif not value:
|
||||
to_remove.append(attr)
|
||||
|
||||
for attr in to_remove:
|
||||
del o[attr]
|
||||
|
||||
remove_null_fields(docdict)
|
||||
|
||||
# retain order of 'fields' table and change order in 'field_order'
|
||||
docdict["field_order"] = [f.fieldname for f in self.fields]
|
||||
|
||||
path = get_file_path(self.module, "DocType", self.name)
|
||||
if os.path.exists(path):
|
||||
try:
|
||||
with open(path, 'r') as txtfile:
|
||||
olddoc = json.loads(txtfile.read())
|
||||
|
||||
old_field_names = [f['fieldname'] for f in olddoc.get("fields", [])]
|
||||
if old_field_names:
|
||||
new_field_dicts = []
|
||||
remaining_field_names = [f.fieldname for f in self.fields]
|
||||
|
||||
for fieldname in old_field_names:
|
||||
field_dict = list(filter(lambda d: d['fieldname'] == fieldname, docdict['fields']))
|
||||
if field_dict:
|
||||
new_field_dicts.append(field_dict[0])
|
||||
remaining_field_names.remove(fieldname)
|
||||
|
||||
for fieldname in remaining_field_names:
|
||||
field_dict = list(filter(lambda d: d['fieldname'] == fieldname, docdict['fields']))
|
||||
new_field_dicts.append(field_dict[0])
|
||||
|
||||
docdict['fields'] = new_field_dicts
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def prepare_for_import(docdict):
|
||||
# set order of fields from field_order
|
||||
if docdict.get("field_order"):
|
||||
new_field_dicts = []
|
||||
remaining_field_names = [f['fieldname'] for f in docdict.get('fields', [])]
|
||||
|
||||
for fieldname in docdict.get('field_order'):
|
||||
field_dict = list(filter(lambda d: d['fieldname'] == fieldname, docdict.get('fields', [])))
|
||||
if field_dict:
|
||||
new_field_dicts.append(field_dict[0])
|
||||
remaining_field_names.remove(fieldname)
|
||||
|
||||
for fieldname in remaining_field_names:
|
||||
field_dict = list(filter(lambda d: d['fieldname'] == fieldname, docdict.get('fields', [])))
|
||||
new_field_dicts.append(field_dict[0])
|
||||
|
||||
docdict['fields'] = new_field_dicts
|
||||
|
||||
if "field_order" in docdict:
|
||||
del docdict["field_order"]
|
||||
|
||||
def export_doc(self):
|
||||
"""Export to standard folder `[module]/doctype/[name]/[name].json`."""
|
||||
from frappe.modules.export_file import export_to_files
|
||||
|
|
@ -545,7 +616,9 @@ def validate_fields(meta):
|
|||
frappe.throw(_("Options 'Dynamic Link' type of field must point to another Link Field with options as 'DocType'"))
|
||||
|
||||
def check_illegal_default(d):
|
||||
if d.fieldtype == "Check" and d.default and d.default not in ('0', '1'):
|
||||
if d.fieldtype == "Check" and not d.default:
|
||||
d.default = '0'
|
||||
if d.fieldtype == "Check" and d.default not in ('0', '1'):
|
||||
frappe.throw(_("Default for 'Check' type of field must be either '0' or '1'"))
|
||||
if d.fieldtype == "Select" and d.default and (d.default not in d.options.split("\n")):
|
||||
frappe.throw(_("Default for {0} must be an option").format(d.fieldname))
|
||||
|
|
|
|||
|
|
@ -104,4 +104,125 @@ class TestDocType(unittest.TestCase):
|
|||
for depends_on in ["depends_on", "collapsible_depends_on"]:
|
||||
condition = field.get(depends_on)
|
||||
if condition:
|
||||
self.assertFalse(re.match(pattern, condition))
|
||||
self.assertFalse(re.match(pattern, condition))
|
||||
|
||||
def test_sync_field_order(self):
|
||||
from frappe.modules.import_file import get_file_path
|
||||
import os
|
||||
|
||||
# create test doctype
|
||||
test_doctype = frappe.get_doc({
|
||||
"doctype": "DocType",
|
||||
"module": "Core",
|
||||
"fields": [
|
||||
{
|
||||
"label": "Field 1",
|
||||
"fieldname": "field_1",
|
||||
"fieldtype": "Data"
|
||||
},
|
||||
{
|
||||
"label": "Field 2",
|
||||
"fieldname": "field_2",
|
||||
"fieldtype": "Data"
|
||||
},
|
||||
{
|
||||
"label": "Field 3",
|
||||
"fieldname": "field_3",
|
||||
"fieldtype": "Data"
|
||||
},
|
||||
{
|
||||
"label": "Field 4",
|
||||
"fieldname": "field_4",
|
||||
"fieldtype": "Data"
|
||||
}
|
||||
],
|
||||
"permissions": [{
|
||||
"role": "System Manager",
|
||||
"read": 1
|
||||
}],
|
||||
"name": "Test Field Order DocType",
|
||||
"__islocal": 1
|
||||
})
|
||||
|
||||
path = get_file_path(test_doctype.module, test_doctype.doctype, test_doctype.name)
|
||||
initial_fields_order = ['field_1', 'field_2', 'field_3', 'field_4']
|
||||
|
||||
frappe.delete_doc_if_exists("DocType", "Test Field Order DocType")
|
||||
if os.path.isfile(path):
|
||||
os.remove(path)
|
||||
|
||||
try:
|
||||
frappe.flags.allow_doctype_export = 1
|
||||
test_doctype.save()
|
||||
|
||||
# assert that field_order list is being created with the default order
|
||||
test_doctype_json = frappe.get_file_json(path)
|
||||
self.assertTrue(test_doctype_json.get("field_order"))
|
||||
self.assertEqual(len(test_doctype_json['fields']), len(test_doctype_json['field_order']))
|
||||
self.assertListEqual([f['fieldname'] for f in test_doctype_json['fields']], test_doctype_json['field_order'])
|
||||
self.assertListEqual([f['fieldname'] for f in test_doctype_json['fields']], initial_fields_order)
|
||||
self.assertListEqual(test_doctype_json['field_order'], initial_fields_order)
|
||||
|
||||
# remove field_order to test reload_doc/sync/migrate is backwards compatible without field_order
|
||||
del test_doctype_json['field_order']
|
||||
with open(path, 'w+') as txtfile:
|
||||
txtfile.write(frappe.as_json(test_doctype_json))
|
||||
|
||||
# assert that field_order is actually removed from the json file
|
||||
test_doctype_json = frappe.get_file_json(path)
|
||||
self.assertFalse(test_doctype_json.get("field_order"))
|
||||
|
||||
# make sure that migrate/sync is backwards compatible without field_order
|
||||
frappe.reload_doctype(test_doctype.name, force=True)
|
||||
test_doctype.reload()
|
||||
|
||||
# assert that field_order list is being created with the default order again
|
||||
test_doctype.save()
|
||||
test_doctype_json = frappe.get_file_json(path)
|
||||
self.assertTrue(test_doctype_json.get("field_order"))
|
||||
self.assertEqual(len(test_doctype_json['fields']), len(test_doctype_json['field_order']))
|
||||
self.assertListEqual([f['fieldname'] for f in test_doctype_json['fields']], test_doctype_json['field_order'])
|
||||
self.assertListEqual([f['fieldname'] for f in test_doctype_json['fields']], initial_fields_order)
|
||||
self.assertListEqual(test_doctype_json['field_order'], initial_fields_order)
|
||||
|
||||
# reorder fields: swap row 1 and 3
|
||||
test_doctype.fields[0], test_doctype.fields[2] = test_doctype.fields[2], test_doctype.fields[0]
|
||||
for i, f in enumerate(test_doctype.fields):
|
||||
f.idx = i + 1
|
||||
|
||||
# assert that reordering fields only affects `field_order` rather than `fields` attr
|
||||
test_doctype.save()
|
||||
test_doctype_json = frappe.get_file_json(path)
|
||||
self.assertListEqual([f['fieldname'] for f in test_doctype_json['fields']], initial_fields_order)
|
||||
self.assertListEqual(test_doctype_json['field_order'], ['field_3', 'field_2', 'field_1', 'field_4'])
|
||||
|
||||
# reorder `field_order` in the json file: swap row 2 and 4
|
||||
test_doctype_json['field_order'][1], test_doctype_json['field_order'][3] = test_doctype_json['field_order'][3], test_doctype_json['field_order'][1]
|
||||
with open(path, 'w+') as txtfile:
|
||||
txtfile.write(frappe.as_json(test_doctype_json))
|
||||
|
||||
# assert that reordering `field_order` from json file is reflected in DocType upon migrate/sync
|
||||
frappe.reload_doctype(test_doctype.name, force=True)
|
||||
test_doctype.reload()
|
||||
self.assertListEqual([f.fieldname for f in test_doctype.fields], ['field_3', 'field_4', 'field_1', 'field_2'])
|
||||
|
||||
# insert row in the middle and remove first row (field 3)
|
||||
test_doctype.append("fields", {
|
||||
"label": "Field 5",
|
||||
"fieldname": "field_5",
|
||||
"fieldtype": "Data"
|
||||
})
|
||||
test_doctype.fields[4], test_doctype.fields[3] = test_doctype.fields[3], test_doctype.fields[4]
|
||||
test_doctype.fields[3], test_doctype.fields[2] = test_doctype.fields[2], test_doctype.fields[3]
|
||||
test_doctype.remove(test_doctype.fields[0])
|
||||
for i, f in enumerate(test_doctype.fields):
|
||||
f.idx = i + 1
|
||||
|
||||
test_doctype.save()
|
||||
test_doctype_json = frappe.get_file_json(path)
|
||||
self.assertListEqual([f['fieldname'] for f in test_doctype_json['fields']], ['field_1', 'field_2', 'field_4', 'field_5'])
|
||||
self.assertListEqual(test_doctype_json['field_order'], ['field_4', 'field_5', 'field_1', 'field_2'])
|
||||
except:
|
||||
raise
|
||||
finally:
|
||||
frappe.flags.allow_doctype_export = 0
|
||||
|
|
|
|||
|
|
@ -1,125 +1,47 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2017-01-13 04:55:18.835023",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"creation": "2017-01-13 04:55:18.835023",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"link_doctype",
|
||||
"link_name",
|
||||
"link_title"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "link_doctype",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Link 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": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "link_doctype",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Link DocType",
|
||||
"options": "DocType",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "link_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Link Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "link_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": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "link_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Link Name",
|
||||
"options": "link_doctype",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "link_title",
|
||||
"fieldtype": "Read Only",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Link Title",
|
||||
"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
|
||||
"fieldname": "link_title",
|
||||
"fieldtype": "Read Only",
|
||||
"in_list_view": 1,
|
||||
"label": "Link Title",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-01-17 14:25:49.140730",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Dynamic Link",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
],
|
||||
"istable": 1,
|
||||
"modified": "2019-05-16 19:54:31.400026",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Dynamic Link",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
// Copyright (c) 2016, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Feedback Request', {
|
||||
refresh: function(frm) {
|
||||
var rating_icons = frappe.render_template("rating_icons", {rating: frm.doc.rating, show_label: false});
|
||||
$(frm.fields_dict.feedback_rating.wrapper).html(rating_icons);
|
||||
|
||||
if(frm.doc.reference_doctype && frm.doc.reference_name) {
|
||||
frm.add_custom_button(__(frm.doc.reference_name), function() {
|
||||
frappe.set_route("Form", frm.doc.reference_doctype, frm.doc.reference_name);
|
||||
});
|
||||
}
|
||||
|
||||
if(frm.doc.reference_communication){
|
||||
frm.add_custom_button(__("Communication"), function() {
|
||||
frappe.set_route("Form", "Communication", frm.doc.reference_communication);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -1,470 +0,0 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "",
|
||||
"beta": 0,
|
||||
"creation": "2017-01-27 15:43:33.780808",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "is_sent",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Is Sent",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "is_feedback_submitted",
|
||||
"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": "Feedback Submitted",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "Is Feedback request triggered manually ?",
|
||||
"fieldname": "is_manual",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Is Manual",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_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,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "key",
|
||||
"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": "Key",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "reference",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Reference",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fieldname": "reference_doctype",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Reference DocType",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fieldname": "reference_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Reference Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_5",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "feedback_trigger",
|
||||
"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": "Feedback Trigger",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Feedback Trigger",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"collapsible_depends_on": "eval: doc.rating",
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_1",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Feedback Rating",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"fieldname": "rating",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Rating",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "feedback_rating",
|
||||
"fieldtype": "HTML",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Feedback Rating",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "reference_communication",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Reference Communication",
|
||||
"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
|
||||
}
|
||||
],
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 1,
|
||||
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-03-03 08:11:09.718589",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Feedback Request",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 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": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "reference_name",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
}
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import get_datetime
|
||||
|
||||
class FeedbackRequest(Document):
|
||||
def autoname(self):
|
||||
""" feedback request name in the format Feedback for {doctype} {name} on {datetime}"""
|
||||
|
||||
self.name = "Feedback for {doctype} {docname} on {datetime}".format(
|
||||
doctype=self.reference_doctype,
|
||||
docname=self.reference_name,
|
||||
datetime=get_datetime()
|
||||
)
|
||||
|
||||
def before_insert(self):
|
||||
from frappe.utils import random_string
|
||||
|
||||
self.key = random_string(32)
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
def is_valid_feedback_request(key=None):
|
||||
if not key:
|
||||
return False
|
||||
|
||||
is_feedback_submitted = frappe.db.get_value("Feedback Request", { "key": key }, "is_feedback_submitted")
|
||||
if is_feedback_submitted:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def delete_feedback_request():
|
||||
""" clear 100 days old feedback request """
|
||||
frappe.db.sql("""delete from `tabFeedback Request` where `creation` < (NOW() - INTERVAL '100' DAY)""")
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
frappe.listview_settings['Feedback Request'] = {
|
||||
colwidths: {
|
||||
subject: 2,
|
||||
},
|
||||
column_render: {
|
||||
rating: function(doc) {
|
||||
var html = ""
|
||||
for (var i = 0; i < 5; i++) {
|
||||
html += repl("<span class='indicator %(color)s'></span>",
|
||||
{color: i<doc.rating? "blue": "darkgrey"})
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('Feedback Request')
|
||||
|
||||
class TestFeedbackRequest(unittest.TestCase):
|
||||
pass
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
// Copyright (c) 2016, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Feedback Trigger', {
|
||||
onload: function(frm) {
|
||||
frm.set_query("document_type", function() {
|
||||
return {
|
||||
"filters": {
|
||||
"istable": 0
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
frm.events.setup_field_options(frm);
|
||||
},
|
||||
|
||||
document_type: function(frm) {
|
||||
frm.set_value('email_field', '');
|
||||
frm.set_value('email_fieldname');
|
||||
|
||||
frm.events.setup_field_options(frm);
|
||||
},
|
||||
|
||||
email_field: function(frm) {
|
||||
frm.set_value('email_fieldname', frm.fieldname_mapper[frm.doc.email_field]);
|
||||
},
|
||||
|
||||
setup_field_options: function(frm) {
|
||||
frm.fieldname_mapper = {};
|
||||
frm.options = [];
|
||||
|
||||
if(!frm.doc.document_type)
|
||||
return
|
||||
|
||||
frappe.model.with_doctype(frm.doc.document_type, function() {
|
||||
var fields = frappe.get_doc("DocType", frm.doc.document_type).fields;
|
||||
$.each(fields, function(idx, field) {
|
||||
if(!in_list(frappe.model.no_value_type, field.fieldtype) && field.options == "Email") {
|
||||
frm.options.push(field.label);
|
||||
frm.fieldname_mapper[field.label] = field.fieldname;
|
||||
}
|
||||
})
|
||||
|
||||
frm.set_df_property("email_field", "options", [""].concat(frm.options));
|
||||
frm.refresh_fields();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -1,517 +0,0 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "field:document_type",
|
||||
"beta": 0,
|
||||
"creation": "2017-01-24 15:46:38.366213",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Setup",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "enabled",
|
||||
"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": "Enabled",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_2",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "document_type",
|
||||
"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": "Document Type",
|
||||
"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": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval: doc.document_type",
|
||||
"fieldname": "email_field",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Email Field",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "email_fieldname",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Email Fieldname",
|
||||
"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
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_5",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "To add dynamic subject, use jinja tags like\n\n<div><pre><code>{{ doc.name }} Delivered</code></pre></div>",
|
||||
"fieldname": "subject",
|
||||
"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": "Subject",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_5",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "1",
|
||||
"description": "Send Feedback Request only if there is at least one communication is available for the document.",
|
||||
"fieldname": "check_communication",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Check Communication",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "Optional: The alert will be sent if this expression is true",
|
||||
"fieldname": "condition",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Condition",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_7",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "html_8",
|
||||
"fieldtype": "HTML",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "<p><strong>Condition Examples:</strong></p>\n<pre>doc.status==\"Closed\"\ndoc.due_date==nowdate()\ndoc.total > 40000\n</pre>",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "section_break_9",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Message",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "message",
|
||||
"fieldtype": "Code",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Message",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "example",
|
||||
"fieldtype": "HTML",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Example",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "<h5>Message Example</h5>\n\n<pre><h3>Issue Resolved</h3>\n\n<p>Issue {{ doc.name }} Is resolved. Please check and confirm the same.</p>\n\n<p> Your Feedback is important for us. Please give us your Feedback for {{ doc.name }}</p>\n\n<h4>Details</h4></pre>",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"idx": 0,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-05-29 16:36:04.178592",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Feedback Trigger",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "document_type",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
}
|
||||
|
|
@ -1,215 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import json
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import get_url
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils.jinja import validate_template
|
||||
|
||||
class FeedbackTrigger(Document):
|
||||
def validate(self):
|
||||
frappe.cache().delete_value('feedback_triggers')
|
||||
validate_template(self.subject)
|
||||
validate_template(self.message)
|
||||
self.validate_condition()
|
||||
|
||||
def on_trash(self):
|
||||
frappe.cache().delete_value('feedback_triggers')
|
||||
|
||||
def validate_condition(self):
|
||||
temp_doc = frappe.new_doc(self.document_type)
|
||||
if self.condition:
|
||||
try:
|
||||
frappe.safe_eval(self.condition, None, get_context(temp_doc))
|
||||
except:
|
||||
frappe.throw(_("The condition '{0}' is invalid").format(self.condition))
|
||||
|
||||
def trigger_feedback_request(doc, method):
|
||||
"""Trigger the feedback alert, or delete feedback requests on delete"""
|
||||
|
||||
def _get():
|
||||
triggers = {}
|
||||
if not (frappe.flags.in_migrate or frappe.flags.in_install):
|
||||
for d in frappe.get_all('Feedback Trigger', dict(enabled=1), ['name', 'document_type']):
|
||||
triggers[d.document_type] = d.name
|
||||
|
||||
return triggers
|
||||
|
||||
feedback_triggers = frappe.cache().get_value('feedback_triggers', _get)
|
||||
if doc.doctype in feedback_triggers:
|
||||
if doc.flags.in_delete:
|
||||
frappe.enqueue('frappe.core.doctype.feedback_trigger.feedback_trigger.delete_feedback_request_and_feedback',
|
||||
reference_doctype=doc.doctype, reference_name=doc.name, now=frappe.flags.in_test)
|
||||
else:
|
||||
frappe.enqueue('frappe.core.doctype.feedback_trigger.feedback_trigger.send_feedback_request',
|
||||
trigger=feedback_triggers[doc.doctype], reference_doctype=doc.doctype,
|
||||
reference_name=doc.name, now=frappe.flags.in_test)
|
||||
|
||||
@frappe.whitelist()
|
||||
def send_feedback_request(reference_doctype, reference_name, trigger="Manual", details=None, is_manual=False):
|
||||
""" send feedback alert """
|
||||
|
||||
if is_feedback_request_already_sent(reference_doctype, reference_name, is_manual=is_manual):
|
||||
frappe.msgprint(_("Feedback Request is already sent to user"))
|
||||
return None
|
||||
|
||||
details = json.loads(details) if details else \
|
||||
get_feedback_request_details(reference_doctype, reference_name, trigger=trigger)
|
||||
|
||||
if not details:
|
||||
return None
|
||||
|
||||
feedback_request, url = get_feedback_request_url(reference_doctype,
|
||||
reference_name, details.get("recipients"), trigger)
|
||||
|
||||
feedback_msg = frappe.render_template("templates/emails/feedback_request_url.html", { "url": url })
|
||||
|
||||
# appending feedback url to message body
|
||||
message = "{message}{feedback_msg}".format(
|
||||
message=details.get("message"),
|
||||
feedback_msg=feedback_msg
|
||||
)
|
||||
details.update({
|
||||
"message": message,
|
||||
"header": [details.get('subject'), 'blue']
|
||||
})
|
||||
|
||||
if details:
|
||||
frappe.sendmail(**details)
|
||||
frappe.db.set_value("Feedback Request", feedback_request, "is_sent", 1)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_feedback_request_details(reference_doctype, reference_name, trigger="Manual", request=None):
|
||||
if not frappe.db.get_value(reference_doctype, reference_name):
|
||||
# reference document is either deleted or renamed
|
||||
return
|
||||
elif not trigger and not request and not frappe.db.get_value("Feedback Trigger", { "document_type": reference_doctype }):
|
||||
return
|
||||
elif not trigger and request:
|
||||
trigger = frappe.db.get_value("Feedback Request", request, "feedback_trigger")
|
||||
else:
|
||||
trigger = frappe.db.get_value("Feedback Trigger", { "document_type": reference_doctype })
|
||||
|
||||
if not trigger:
|
||||
return
|
||||
|
||||
feedback_trigger = frappe.get_doc("Feedback Trigger", trigger)
|
||||
|
||||
doc = frappe.get_doc(reference_doctype, reference_name)
|
||||
context = get_context(doc)
|
||||
|
||||
recipients = doc.get(feedback_trigger.email_fieldname, None)
|
||||
if feedback_trigger.check_communication:
|
||||
communications = frappe.get_all("Communication", filters={
|
||||
"reference_doctype": reference_doctype,
|
||||
"reference_name": reference_name,
|
||||
"communication_type": "Communication",
|
||||
"sent_or_received": "Sent"
|
||||
}, fields=["name"])
|
||||
|
||||
if len(communications) < 1:
|
||||
frappe.msgprint(_("At least one reply is mandatory before requesting feedback"))
|
||||
return None
|
||||
|
||||
if recipients and (not feedback_trigger.condition or \
|
||||
frappe.safe_eval(feedback_trigger.condition, None, context)):
|
||||
subject = feedback_trigger.subject
|
||||
context.update({ "feedback_trigger": feedback_trigger })
|
||||
|
||||
if "{" in subject:
|
||||
subject = frappe.render_template(feedback_trigger.subject, context)
|
||||
|
||||
feedback_request_message = frappe.render_template(feedback_trigger.message, context)
|
||||
|
||||
return {
|
||||
"subject": subject,
|
||||
"recipients": recipients,
|
||||
"reference_name":doc.name,
|
||||
"reference_doctype":doc.doctype,
|
||||
"message": feedback_request_message,
|
||||
}
|
||||
else:
|
||||
frappe.msgprint(_("Feedback conditions do not match"))
|
||||
return None
|
||||
|
||||
def get_feedback_request_url(reference_doctype, reference_name, recipients, trigger="Manual"):
|
||||
""" prepare the feedback request url """
|
||||
is_manual = 1 if trigger == "Manual" else 0
|
||||
feedback_request = frappe.get_doc({
|
||||
"is_manual": is_manual,
|
||||
"feedback_trigger": trigger,
|
||||
"doctype": "Feedback Request",
|
||||
"reference_name": reference_name,
|
||||
"reference_doctype": reference_doctype,
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
feedback_url = "{base_url}/feedback?reference_doctype={doctype}&reference_name={docname}&email={email_id}&key={nonce}".format(
|
||||
base_url=get_url(),
|
||||
doctype=reference_doctype,
|
||||
docname=reference_name,
|
||||
email_id=recipients,
|
||||
nonce=feedback_request.key
|
||||
)
|
||||
|
||||
return [ feedback_request.name, feedback_url ]
|
||||
|
||||
def is_feedback_request_already_sent(reference_doctype, reference_name, is_manual=False):
|
||||
"""
|
||||
check if feedback request mail is already sent but feedback is not submitted
|
||||
to avoid sending multiple feedback request mail
|
||||
"""
|
||||
is_request_sent = False
|
||||
filters = {
|
||||
"is_sent": 1,
|
||||
"reference_name": reference_name,
|
||||
"is_manual": 1 if is_manual else 0,
|
||||
"reference_doctype": reference_doctype
|
||||
}
|
||||
|
||||
if is_manual:
|
||||
filters.update({ "is_feedback_submitted": 0 })
|
||||
|
||||
feedback_request = frappe.get_all("Feedback Request", filters=filters, fields=["name"])
|
||||
|
||||
if feedback_request: is_request_sent = True
|
||||
return is_request_sent
|
||||
|
||||
def get_enabled_feedback_trigger():
|
||||
""" get mapper of all the enable feedback trigger """
|
||||
|
||||
triggers = frappe.get_all("Feedback Trigger", filters={"enabled": 1},
|
||||
fields=["document_type", "name"], as_list=True)
|
||||
|
||||
triggers = { dt[0]: dt[1] for dt in triggers }
|
||||
return triggers
|
||||
|
||||
def get_context(doc):
|
||||
return { "doc": doc }
|
||||
|
||||
def delete_feedback_request_and_feedback(reference_doctype, reference_name):
|
||||
""" delete all the feedback request and feedback communication """
|
||||
if not all([reference_doctype, reference_name]):
|
||||
return
|
||||
|
||||
feedback_requests = frappe.get_all("Feedback Request", filters={
|
||||
"is_feedback_submitted": 0,
|
||||
"reference_doctype": reference_doctype,
|
||||
"reference_name": reference_name
|
||||
})
|
||||
|
||||
communications = frappe.get_all("Communication", {
|
||||
"communication_type": "Feedback",
|
||||
"reference_doctype": reference_doctype,
|
||||
"reference_name": reference_name
|
||||
})
|
||||
|
||||
for request in feedback_requests:
|
||||
frappe.delete_doc("Feedback Request", request.get("name"), ignore_permissions=True)
|
||||
|
||||
for communication in communications:
|
||||
frappe.delete_doc("Communication", communication.get("name"), ignore_permissions=True)
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2015, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('Feedback Trigger')
|
||||
def get_feedback_request(todo, feedback_trigger):
|
||||
return frappe.db.get_value("Feedback Request", {
|
||||
"is_sent": 1,
|
||||
"is_feedback_submitted": 0,
|
||||
"reference_doctype": "ToDo",
|
||||
"reference_name": todo,
|
||||
"feedback_trigger": feedback_trigger
|
||||
}, ["name", "key"])
|
||||
|
||||
class TestFeedbackTrigger(unittest.TestCase):
|
||||
def setUp(self):
|
||||
new_user = frappe.get_doc(dict(doctype='User', email='test-feedback@example.com',
|
||||
first_name='Tester')).insert(ignore_permissions=True)
|
||||
new_user.add_roles("System Manager")
|
||||
|
||||
def tearDown(self):
|
||||
frappe.db.sql("delete from tabContact where email_id='test-feedback@example.com'")
|
||||
frappe.delete_doc("User", "test-feedback@example.com")
|
||||
frappe.delete_doc("Feedback Trigger", "ToDo")
|
||||
frappe.db.sql('delete from `tabEmail Queue`')
|
||||
frappe.db.sql('delete from `tabFeedback Request`')
|
||||
|
||||
def test_feedback_trigger(self):
|
||||
""" Test feedback trigger """
|
||||
from frappe.www.feedback import accept
|
||||
|
||||
frappe.delete_doc("Feedback Trigger", "ToDo")
|
||||
frappe.db.sql('delete from `tabEmail Queue`')
|
||||
frappe.db.sql('delete from `tabFeedback Request`')
|
||||
|
||||
feedback_trigger = frappe.get_doc({
|
||||
"enabled": 1,
|
||||
"doctype": "Feedback Trigger",
|
||||
"document_type": "ToDo",
|
||||
"email_field": "assigned_by",
|
||||
"email_fieldname": "assigned_by",
|
||||
"subject": "{{ doc.name }} Task Completed",
|
||||
"condition": "doc.status == 'Closed'",
|
||||
"message": """Task {{ doc.name }} is Completed by {{ doc.owner }}.
|
||||
regarding the Task {{ doc.name }}"""
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
# create a todo
|
||||
todo = frappe.get_doc({
|
||||
"doctype": "ToDo",
|
||||
"owner": "test-feedback@example.com",
|
||||
"assigned_by": "test-feedback@example.com",
|
||||
"description": "Unable To Submit Sales Order #SO-00001"
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
# feedback alert mail should be sent only on 'Closed' status
|
||||
email_queue = frappe.db.sql("""select name from `tabEmail Queue` where
|
||||
reference_doctype='ToDo' and reference_name='{0}'""".format(todo.name))
|
||||
self.assertFalse(email_queue)
|
||||
|
||||
# add a communication
|
||||
frappe.get_doc({
|
||||
"reference_doctype": "ToDo",
|
||||
"reference_name": todo.name,
|
||||
"communication_type": "Communication",
|
||||
"content": "Test Communication",
|
||||
"subject": "Test Communication",
|
||||
"doctype": "Communication"
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
# check if feedback mail alert is triggered
|
||||
todo.reload()
|
||||
todo.status = "Closed"
|
||||
todo.save(ignore_permissions=True)
|
||||
|
||||
email_queue = frappe.db.sql("""select name from `tabEmail Queue` where
|
||||
reference_doctype='ToDo' and reference_name='{0}'""".format(todo.name))
|
||||
self.assertTrue(email_queue)
|
||||
|
||||
# test if feedback is submitted for the todo
|
||||
feedback_request, request_key = get_feedback_request(todo.name, feedback_trigger.name)
|
||||
self.assertTrue(feedback_request)
|
||||
|
||||
# test if mail alerts are triggered multiple times for same document
|
||||
todo.save(ignore_permissions=True)
|
||||
email_queue = frappe.db.sql("""select name from `tabEmail Queue` where
|
||||
reference_doctype='ToDo' and reference_name='{0}'""".format(todo.name))
|
||||
self.assertTrue(len(email_queue) == 1)
|
||||
frappe.db.sql('delete from `tabEmail Queue`')
|
||||
|
||||
|
||||
# Test if feedback is submitted sucessfully
|
||||
result = accept(request_key, "test-feedback@example.com", "ToDo", todo.name, "Great Work !!", 4, fullname="Test User")
|
||||
self.assertTrue(result)
|
||||
|
||||
# test if feedback is saved in Communication
|
||||
docname = frappe.db.get_value("Communication", {
|
||||
"reference_doctype": "ToDo",
|
||||
"reference_name": todo.name,
|
||||
"communication_type": "Feedback",
|
||||
"feedback_request": feedback_request
|
||||
})
|
||||
|
||||
communication = frappe.get_doc("Communication", docname)
|
||||
self.assertEqual(communication.rating, 4)
|
||||
self.assertEqual(communication.content, "Great Work !!")
|
||||
|
||||
# test if link expired after feedback submission
|
||||
self.assertRaises(Exception, accept, key=request_key, sender="test-feedback@example.com",
|
||||
reference_doctype="ToDo", reference_name=todo.name, feedback="Thank You !!", rating=4, fullname="Test User")
|
||||
|
||||
# auto feedback request should trigger only once
|
||||
todo.reload()
|
||||
todo.save(ignore_permissions=True)
|
||||
email_queue = frappe.db.sql("""select name from `tabEmail Queue` where
|
||||
reference_doctype='ToDo' and reference_name='{0}'""".format(todo.name))
|
||||
self.assertFalse(email_queue)
|
||||
frappe.delete_doc("ToDo", todo.name)
|
||||
|
||||
# test if feedback requests and feedback communications are deleted?
|
||||
communications = frappe.get_all("Communication", {
|
||||
"reference_doctype": "ToDo",
|
||||
"reference_name": todo.name,
|
||||
"communication_type": "Feedback"
|
||||
})
|
||||
self.assertFalse(communications)
|
||||
|
||||
feedback_requests = frappe.get_all("Feedback Request", {
|
||||
"reference_doctype": "ToDo",
|
||||
"reference_name": todo.name,
|
||||
"is_feedback_submitted": 0
|
||||
})
|
||||
self.assertFalse(feedback_requests)
|
||||
|
|
@ -290,6 +290,8 @@ class File(NestedSet):
|
|||
|
||||
zip_path = frappe.get_site_path(self.file_url.strip('/'))
|
||||
base_url = os.path.dirname(self.file_url)
|
||||
|
||||
files = []
|
||||
with zipfile.ZipFile(zip_path) as zf:
|
||||
zf.extractall(os.path.dirname(zip_path))
|
||||
for info in zf.infolist():
|
||||
|
|
@ -308,8 +310,10 @@ class File(NestedSet):
|
|||
file_doc.attached_to_doctype = self.attached_to_doctype
|
||||
file_doc.attached_to_name = self.attached_to_name
|
||||
file_doc.save()
|
||||
files.append(file_doc)
|
||||
|
||||
frappe.delete_doc('File', self.name)
|
||||
return files
|
||||
|
||||
|
||||
def get_file_url(self):
|
||||
|
|
@ -888,7 +892,8 @@ def get_random_filename(extn=None, content_type=None):
|
|||
def unzip_file(name):
|
||||
'''Unzip the given file and make file records for each of the extracted files'''
|
||||
file_obj = frappe.get_doc('File', name)
|
||||
file_obj.unzip()
|
||||
files = file_obj.unzip()
|
||||
return len(files)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
|
@ -919,3 +924,10 @@ def validate_filename(filename):
|
|||
timestamp = now_datetime().strftime(" %Y-%m-%d %H:%M:%S")
|
||||
fname = get_file_name(filename, timestamp)
|
||||
return fname
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_files_in_folder(folder):
|
||||
return frappe.db.get_all('File',
|
||||
{ 'folder': folder },
|
||||
['name', 'file_name', 'file_url', 'is_folder', 'modified']
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,9 +3,14 @@
|
|||
|
||||
frappe.ui.form.on('Page', {
|
||||
refresh: function(frm) {
|
||||
if(!frappe.boot.developer_mode && user != 'Administrator') {
|
||||
if (!frappe.boot.developer_mode && frappe.session.user != 'Administrator') {
|
||||
// make the document read-only
|
||||
frm.set_read_only();
|
||||
}
|
||||
if (!frm.is_new() && !frm.doc.istable) {
|
||||
frm.add_custom_button(__('Go to {0} Page', [frm.doc.title || frm.doc.name]), () => {
|
||||
frappe.set_route(frm.doc.name);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "report_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
|
|
@ -53,6 +54,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "ref_report_doctype",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
|
|
@ -86,6 +88,8 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Queued",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 1,
|
||||
|
|
@ -93,12 +97,12 @@
|
|||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Status",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Queued\nCompleted",
|
||||
"options": "Error\nQueued\nCompleted",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
|
|
@ -119,6 +123,39 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "report_start_time",
|
||||
"fieldtype": "Datetime",
|
||||
"hidden": 0,
|
||||
|
|
@ -151,6 +188,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "report_end_time",
|
||||
"fieldtype": "Datetime",
|
||||
"hidden": 0,
|
||||
|
|
@ -183,6 +221,73 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.status == 'Error'",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "section_break_7",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "error_message",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Error Message",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "filters_sb",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
|
|
@ -215,6 +320,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "filters",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
|
|
@ -247,6 +353,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "filter_values",
|
||||
"fieldtype": "HTML",
|
||||
"hidden": 0,
|
||||
|
|
@ -279,6 +386,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "columns",
|
||||
"fieldtype": "Code",
|
||||
"hidden": 1,
|
||||
|
|
@ -315,7 +423,7 @@
|
|||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-10-23 16:58:14.879417",
|
||||
"modified": "2019-04-19 12:39:47.211516",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Prepared Report",
|
||||
|
|
|
|||
|
|
@ -25,25 +25,43 @@ class PreparedReport(Document):
|
|||
self.status = "Queued"
|
||||
self.report_start_time = frappe.utils.now()
|
||||
|
||||
def after_insert(self):
|
||||
def enqueue_report(self):
|
||||
enqueue(
|
||||
run_background,
|
||||
instance=self, timeout=6000
|
||||
prepared_report=self.name, timeout=6000
|
||||
)
|
||||
|
||||
def on_trash(self):
|
||||
remove_all("PreparedReport", self.name, from_delete=True)
|
||||
|
||||
|
||||
def run_background(instance):
|
||||
def run_background(prepared_report):
|
||||
instance = frappe.get_doc("Prepared Report", prepared_report)
|
||||
report = frappe.get_doc("Report", instance.ref_report_doctype)
|
||||
result = generate_report_result(report, filters=instance.filters, user=instance.owner)
|
||||
create_json_gz_file(result['result'], 'Prepared Report', instance.name)
|
||||
|
||||
instance.status = "Completed"
|
||||
instance.columns = json.dumps(result["columns"])
|
||||
instance.report_end_time = frappe.utils.now()
|
||||
instance.save()
|
||||
try:
|
||||
report.custom_columns = []
|
||||
|
||||
if report.report_type == 'Custom Report':
|
||||
custom_report_doc = report
|
||||
reference_report = custom_report_doc.reference_report
|
||||
report = frappe.get_doc("Report", reference_report)
|
||||
report.custom_columns = custom_report_doc.json
|
||||
|
||||
result = generate_report_result(report, filters=instance.filters, user=instance.owner)
|
||||
create_json_gz_file(result['result'], 'Prepared Report', instance.name)
|
||||
|
||||
instance.status = "Completed"
|
||||
instance.columns = json.dumps(result["columns"])
|
||||
instance.report_end_time = frappe.utils.now()
|
||||
instance.save(ignore_permissions=True)
|
||||
|
||||
except Exception:
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
instance = frappe.get_doc("Prepared Report", prepared_report)
|
||||
instance.status = "Error"
|
||||
instance.error_message = frappe.get_traceback()
|
||||
instance.save(ignore_permissions=True)
|
||||
|
||||
frappe.publish_realtime(
|
||||
'report_generated',
|
||||
|
|
|
|||
12
frappe/core/doctype/prepared_report/prepared_report_list.js
Normal file
12
frappe/core/doctype/prepared_report/prepared_report_list.js
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
frappe.listview_settings['Prepared Report'] = {
|
||||
add_fields: ["status"],
|
||||
get_indicator: function(doc) {
|
||||
if(doc.status==="Completed"){
|
||||
return [__("Completed"), "green", "status,=,Completed"];
|
||||
} else if(doc.status ==="Error"){
|
||||
return [__("Error"), "red", "status,=,Error"];
|
||||
} else if(doc.status ==="Queued"){
|
||||
return [__("Queued"), "orange", "status,=,Queued"];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -6,4 +6,4 @@ frappe.query_reports["{name}"] = {{
|
|||
"filters": [
|
||||
|
||||
]
|
||||
}}
|
||||
}};
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
# import frappe
|
||||
|
||||
def execute(filters=None):
|
||||
columns, data = [], []
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ class Report(Document):
|
|||
columns = []
|
||||
out = []
|
||||
|
||||
if self.report_type in ('Query Report', 'Script Report'):
|
||||
if self.report_type in ('Query Report', 'Script Report', 'Custom Report'):
|
||||
# query and script reports
|
||||
data = frappe.desk.query_report.run(self.name, filters=filters, user=user)
|
||||
for d in data.get('columns'):
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ from frappe.utils.user import get_system_managers
|
|||
import frappe.permissions
|
||||
import frappe.share
|
||||
import re
|
||||
import json
|
||||
|
||||
from frappe.limits import get_limits
|
||||
from frappe.website.utils import is_signup_enabled
|
||||
from frappe.utils.background_jobs import enqueue
|
||||
|
|
@ -1086,4 +1088,12 @@ def generate_keys(user):
|
|||
user_details.save()
|
||||
|
||||
return {"api_secret": api_secret}
|
||||
frappe.throw(frappe._("Not Permitted"), frappe.PermissionError)
|
||||
frappe.throw(frappe._("Not Permitted"), frappe.PermissionError)
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_profile_info(profile_info):
|
||||
profile_info = json.loads(profile_info)
|
||||
user = frappe.get_doc('User', frappe.session.user)
|
||||
user.update(profile_info)
|
||||
user.save()
|
||||
return user
|
||||
|
|
@ -16,4 +16,4 @@ frappe.listview_settings['User'] = {
|
|||
}
|
||||
};
|
||||
|
||||
frappe.help.youtube_id["User"] = "fnBoRhBrwR4";
|
||||
frappe.help.youtube_id["User"] = "8Slw1hsTmUI";
|
||||
|
|
|
|||
|
|
@ -8,17 +8,26 @@ import frappe
|
|||
import unittest
|
||||
|
||||
class TestUserPermission(unittest.TestCase):
|
||||
def test_default_user_permission_validation(self):
|
||||
user = create_user('test_default_permission@example.com')
|
||||
param = get_params(user, 'User', user.name, is_default=1)
|
||||
add_user_permissions(param)
|
||||
#create a duplicate entry with default
|
||||
perm_user = create_user('test_user_perm@example.com')
|
||||
param = get_params(user, 'User', perm_user.name, is_default=1)
|
||||
self.assertRaises(frappe.ValidationError, add_user_permissions, param)
|
||||
|
||||
def test_apply_to_all(self):
|
||||
''' Create User permission for User having access to all applicable Doctypes'''
|
||||
user = get_user()
|
||||
param = get_params(user, apply = 1)
|
||||
user = create_user('test_bulk_creation_update@example.com')
|
||||
param = get_params(user, 'User', user.name)
|
||||
created = add_user_permissions(param)
|
||||
self.assertEquals(created, 1)
|
||||
|
||||
def test_for_applicable_on_update_from_apply_to_all(self):
|
||||
''' Update User Permission from all to some applicable Doctypes'''
|
||||
user = get_user()
|
||||
param = get_params(user, applicable = ["Chat Room", "Chat Message"])
|
||||
user = create_user('test_bulk_creation_update@example.com')
|
||||
param = get_params(user, 'User', user.name , applicable = ["Chat Room", "Chat Message"])
|
||||
create = add_user_permissions(param)
|
||||
frappe.db.commit()
|
||||
|
||||
|
|
@ -33,8 +42,8 @@ class TestUserPermission(unittest.TestCase):
|
|||
|
||||
def test_for_apply_to_all_on_update_from_applicable(self):
|
||||
''' Update User Permission from some to all applicable Doctypes'''
|
||||
user = get_user()
|
||||
param = get_params(user, apply = 1)
|
||||
user = create_user('test_bulk_creation_update@example.com')
|
||||
param = get_params(user, 'User', user.name)
|
||||
created = add_user_permissions(param)
|
||||
created_apply_to_all = frappe.db.exists("User Permission", get_exists_param(user))
|
||||
removed_applicable_first = frappe.db.exists("User Permission", get_exists_param(user, applicable = "Chat Room"))
|
||||
|
|
@ -46,26 +55,27 @@ class TestUserPermission(unittest.TestCase):
|
|||
self.assertIsNone(removed_applicable_second)
|
||||
self.assertEquals(created, 1)
|
||||
|
||||
def get_user():
|
||||
if frappe.db.exists('User', 'test_bulk_creation_update@example.com'):
|
||||
return frappe.get_doc('User', 'test_bulk_creation_update@example.com')
|
||||
def create_user(email):
|
||||
''' create user with role system manager '''
|
||||
if frappe.db.exists('User', email):
|
||||
return frappe.get_doc('User', email)
|
||||
else:
|
||||
user = frappe.new_doc('User')
|
||||
user.email = 'test_bulk_creation_update@example.com'
|
||||
user.first_name = 'Test_Bulk_Creation'
|
||||
user.email = email
|
||||
user.first_name = email.split("@")[0]
|
||||
user.add_roles("System Manager")
|
||||
return user
|
||||
|
||||
def get_params(user, apply = None , applicable = None):
|
||||
def get_params(user, doctype, docname, is_default=0, applicable=None):
|
||||
''' Return param to insert '''
|
||||
param = {
|
||||
"user": user.name,
|
||||
"doctype":"User",
|
||||
"docname":user.name
|
||||
"doctype":doctype,
|
||||
"docname":docname,
|
||||
"is_default": is_default,
|
||||
"apply_to_all_doctypes": 1,
|
||||
"applicable_doctypes": []
|
||||
}
|
||||
if apply:
|
||||
param.update({"apply_to_all_doctypes": 1})
|
||||
param.update({"applicable_doctypes": []})
|
||||
if applicable:
|
||||
param.update({"apply_to_all_doctypes": 0})
|
||||
param.update({"applicable_doctypes": applicable})
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "user",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
|
|
@ -53,6 +54,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "allow",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
|
|
@ -86,6 +88,39 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "for_value",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
|
|
@ -119,6 +154,40 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "is_default",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Is Default",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "advanced_control_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
|
|
@ -152,6 +221,7 @@
|
|||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "1",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "apply_to_all_doctypes",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
|
|
@ -185,6 +255,7 @@
|
|||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:!doc.apply_to_all_doctypes",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "applicable_for",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
|
|
@ -213,16 +284,14 @@
|
|||
}
|
||||
],
|
||||
"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-02-13 22:58:27.428741",
|
||||
"modified": "2019-04-16 19:17:23.644724",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "User Permission",
|
||||
|
|
@ -251,7 +320,6 @@
|
|||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
|
|
|
|||
|
|
@ -7,21 +7,14 @@ import frappe, json
|
|||
from frappe.model.document import Document
|
||||
from frappe.permissions import (get_valid_perms, update_permission_property)
|
||||
from frappe import _
|
||||
from frappe.utils import cstr
|
||||
from frappe.core.utils import find
|
||||
from frappe.desk.form.linked_with import get_linked_doctypes
|
||||
|
||||
class UserPermission(Document):
|
||||
def validate(self):
|
||||
duplicate_exists = frappe.db.get_all(self.doctype, filters={
|
||||
'allow': self.allow,
|
||||
'for_value': self.for_value,
|
||||
'user': self.user,
|
||||
'applicable_for': self.applicable_for,
|
||||
'apply_to_all_doctypes': self.apply_to_all_doctypes,
|
||||
'name': ['!=', self.name]
|
||||
}, limit=1)
|
||||
if duplicate_exists:
|
||||
frappe.throw(_("User permission already exists"), frappe.DuplicateEntryError)
|
||||
self.validate_user_permission()
|
||||
self.validate_default_permission()
|
||||
|
||||
def on_update(self):
|
||||
frappe.cache().delete_value('user_permissions')
|
||||
|
|
@ -31,6 +24,37 @@ class UserPermission(Document):
|
|||
frappe.cache().delete_value('user_permissions')
|
||||
frappe.publish_realtime('update_user_permissions')
|
||||
|
||||
def validate_user_permission(self):
|
||||
''' checks for duplicate user permission records'''
|
||||
|
||||
duplicate_exists = frappe.db.get_all(self.doctype, filters={
|
||||
'allow': self.allow,
|
||||
'for_value': self.for_value,
|
||||
'user': self.user,
|
||||
'applicable_for': cstr(self.applicable_for),
|
||||
'apply_to_all_doctypes': self.apply_to_all_doctypes,
|
||||
'name': ['!=', self.name]
|
||||
}, limit=1)
|
||||
if duplicate_exists:
|
||||
frappe.throw(_("User permission already exists"), frappe.DuplicateEntryError)
|
||||
|
||||
def validate_default_permission(self):
|
||||
''' validate user permission overlap for default value of a particular doctype '''
|
||||
overlap_exists = []
|
||||
if self.is_default:
|
||||
overlap_exists = frappe.get_all(self.doctype, filters={
|
||||
'allow': self.allow,
|
||||
'user': self.user,
|
||||
'is_default': 1,
|
||||
'name': ['!=', self.name]
|
||||
}, or_filters={
|
||||
'applicable_for': cstr(self.applicable_for),
|
||||
'apply_to_all_doctypes': 1
|
||||
}, limit=1)
|
||||
if overlap_exists:
|
||||
ref_link = frappe.get_desk_link(self.doctype, overlap_exists[0].name)
|
||||
frappe.throw(_("{0} has already assigned default value for {1}.".format(ref_link, self.allow)))
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_user_permissions(user=None):
|
||||
'''Get all users permissions for the user as a dict of doctype'''
|
||||
|
|
@ -52,7 +76,7 @@ def get_user_permissions(user=None):
|
|||
|
||||
out = {}
|
||||
|
||||
def add_doc_to_perm(perm, doc_name):
|
||||
def add_doc_to_perm(perm, doc_name, is_default):
|
||||
# group rules for each type
|
||||
# for example if allow is "Customer", then build all allowed customers
|
||||
# in a list
|
||||
|
|
@ -61,21 +85,22 @@ def get_user_permissions(user=None):
|
|||
|
||||
out[perm.allow].append(frappe._dict({
|
||||
'doc': doc_name,
|
||||
'applicable_for': perm.get('applicable_for')
|
||||
'applicable_for': perm.get('applicable_for'),
|
||||
'is_default': is_default
|
||||
}))
|
||||
|
||||
try:
|
||||
for perm in frappe.get_all('User Permission',
|
||||
fields=['allow', 'for_value', 'applicable_for'],
|
||||
fields=['allow', 'for_value', 'applicable_for', 'is_default'],
|
||||
filters=dict(user=user)):
|
||||
|
||||
meta = frappe.get_meta(perm.allow)
|
||||
add_doc_to_perm(perm, perm.for_value)
|
||||
add_doc_to_perm(perm, perm.for_value, perm.is_default)
|
||||
|
||||
if meta.is_nested_set():
|
||||
decendants = frappe.db.get_descendants(perm.allow, perm.for_value)
|
||||
for doc in decendants:
|
||||
add_doc_to_perm(perm, doc)
|
||||
add_doc_to_perm(perm, doc, False)
|
||||
|
||||
out = frappe._dict(out)
|
||||
frappe.cache().hset("user_permissions", user, out)
|
||||
|
|
@ -160,24 +185,25 @@ def add_user_permissions(data):
|
|||
exists = frappe.db.exists("User Permission", {"user": data.user, "allow": data.doctype, "for_value": data.docname, "apply_to_all_doctypes": 1})
|
||||
if data.apply_to_all_doctypes == 1 and not exists:
|
||||
remove_applicable(d, data.user, data.doctype, data.docname)
|
||||
insert_user_perm(data.user, data.doctype, data.docname, apply_to_all = 1)
|
||||
insert_user_perm(data.user, data.doctype, data.docname, data.is_default, apply_to_all = 1)
|
||||
return 1
|
||||
else:
|
||||
remove_apply_to_all(data.user, data.doctype, data.docname)
|
||||
update_applicable(d, data.applicable_doctypes, data.user, data.doctype, data.docname)
|
||||
for applicable in data.applicable_doctypes :
|
||||
if applicable not in d:
|
||||
insert_user_perm(data.user, data.doctype, data.docname, applicable = applicable)
|
||||
insert_user_perm(data.user, data.doctype, data.docname, data.is_default, applicable = applicable)
|
||||
elif exists:
|
||||
insert_user_perm(data.user, data.doctype, data.docname, applicable = applicable)
|
||||
insert_user_perm(data.user, data.doctype, data.docname, data.is_default, applicable = applicable)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def insert_user_perm(user, doctype, docname, apply_to_all=None, applicable=None):
|
||||
def insert_user_perm(user, doctype, docname, is_default=0, apply_to_all=None, applicable=None):
|
||||
user_perm = frappe.new_doc("User Permission")
|
||||
user_perm.user = user
|
||||
user_perm.allow = doctype
|
||||
user_perm.for_value = docname
|
||||
user_perm.is_default = is_default
|
||||
if applicable:
|
||||
user_perm.applicable_for = applicable
|
||||
user_perm.apply_to_all_doctypes = 0
|
||||
|
|
@ -210,4 +236,4 @@ def update_applicable(already_applied, to_apply, user, doctype, docname):
|
|||
AND `applicable_for`=%s
|
||||
AND `allow`=%s
|
||||
AND `for_value`=%s
|
||||
""",(user, applied, doctype, docname))
|
||||
""",(user, applied, doctype, docname))
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ frappe.listview_settings['User Permission'] = {
|
|||
dialog.fields_dict.doctype.set_input(undefined);
|
||||
dialog.fields_dict.docname.set_input(undefined);
|
||||
dialog.set_df_property("docname", "hidden", 1);
|
||||
dialog.set_df_property("is_default", "hidden", 1);
|
||||
dialog.set_df_property("apply_to_all_doctypes", "hidden", 1);
|
||||
dialog.set_df_property("applicable_doctypes", "hidden", 1);
|
||||
}
|
||||
|
|
@ -53,11 +54,16 @@ frappe.listview_settings['User Permission'] = {
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
fieldname: 'is_default',
|
||||
label: __('Is Default'),
|
||||
fieldtype: 'Check',
|
||||
hidden: 1
|
||||
},
|
||||
{
|
||||
fieldname: 'apply_to_all_doctypes',
|
||||
label: __('Apply to all Documents Types'),
|
||||
fieldtype: 'Check',
|
||||
checked: 1,
|
||||
hidden: 1,
|
||||
onchange: function() {
|
||||
if(dialog.fields_dict.doctype.value && dialog.fields_dict.docname.value && dialog.fields_dict.user.value){
|
||||
|
|
@ -205,8 +211,9 @@ frappe.listview_settings['User Permission'] = {
|
|||
on_doctype_change: function(dialog) {
|
||||
dialog.set_df_property("docname", "hidden", 0);
|
||||
dialog.set_df_property("docname", "reqd", 1);
|
||||
dialog.set_df_property("is_default", "hidden", 0);
|
||||
dialog.set_df_property("apply_to_all_doctypes", "hidden", 0);
|
||||
dialog.set_value("apply_to_all_doctypes","checked",1);
|
||||
dialog.set_value("apply_to_all_doctypes", "checked", 1);
|
||||
},
|
||||
|
||||
on_docname_change: function(dialog, options, applicable) {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,9 @@ def get_diff(old, new, for_child=False):
|
|||
],
|
||||
|
||||
}'''
|
||||
if not new:
|
||||
return None
|
||||
|
||||
out = frappe._dict(changed = [], added = [], removed = [], row_changed = [])
|
||||
for df in new.meta.fields:
|
||||
if df.fieldtype in no_value_fields and df.fieldtype not in table_fields:
|
||||
|
|
|
|||
|
|
@ -60,7 +60,12 @@ class Dashboard {
|
|||
show_dashboard(current_dashboard_name) {
|
||||
if(this.dashboard_name !== current_dashboard_name) {
|
||||
this.dashboard_name = current_dashboard_name;
|
||||
this.page.set_title(this.dashboard_name);
|
||||
let title = this.dashboard_name;
|
||||
if (!this.dashboard_name.toLowerCase().includes(__('dashboard'))) {
|
||||
// ensure dashboard title has "dashboard"
|
||||
title = __('{0} Dashboard', [title]);
|
||||
}
|
||||
this.page.set_title(title);
|
||||
this.set_dropdown();
|
||||
this.container.empty();
|
||||
this.refresh();
|
||||
|
|
|
|||
|
|
@ -1,60 +0,0 @@
|
|||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.query_reports["Feedback Ratings"] = {
|
||||
"filters": [
|
||||
{
|
||||
"fieldname": "document_type",
|
||||
"label": __("Document Type"),
|
||||
"fieldtype": "Link",
|
||||
"options": "DocType",
|
||||
"reqd": 1,
|
||||
"default": "Issue",
|
||||
"get_query": function() {
|
||||
return {
|
||||
"query": "frappe.core.report.feedback_ratings.feedback_ratings.get_document_type"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "document_id",
|
||||
"label": __("Document ID"),
|
||||
"fieldtype": "Dynamic Link",
|
||||
"get_options": function() {
|
||||
var document_type = frappe.query_report.get_filter_value('document_type');
|
||||
if(!document_type) {
|
||||
frappe.throw(__("Please select Document Type first"));
|
||||
}
|
||||
return document_type;
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname":"from_date",
|
||||
"label": __("From Date"),
|
||||
"fieldtype": "Date",
|
||||
'reqd': 1,
|
||||
"default": frappe.datetime.add_days(frappe.datetime.nowdate(), -30)
|
||||
},
|
||||
{
|
||||
"fieldname":"to_date",
|
||||
"label": __("To Date"),
|
||||
"fieldtype": "Date",
|
||||
'reqd': 1,
|
||||
"default":frappe.datetime.nowdate()
|
||||
}
|
||||
],
|
||||
|
||||
get_chart_data: function(columns, result) {
|
||||
return {
|
||||
data: {
|
||||
x: 'Date',
|
||||
columns: [
|
||||
['Date'].concat($.map(result, function(d) { return d[0]; })),
|
||||
['Average Feedback'].concat($.map(result, function(d) { return d[1]; }))
|
||||
]
|
||||
},
|
||||
chart_type: 'line',
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2017-02-05 20:38:21.890174",
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 2,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2017-02-24 19:56:51.141147",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Feedback Ratings",
|
||||
"owner": "Administrator",
|
||||
"ref_doctype": "Feedback Trigger",
|
||||
"report_name": "Feedback Ratings",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "System Manager"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute(filters=None):
|
||||
columns, data = get_columns(filters), get_data(filters)
|
||||
return columns, data
|
||||
|
||||
def get_columns(filters):
|
||||
return [
|
||||
"Date:Date",
|
||||
"Average Rating",
|
||||
]
|
||||
|
||||
def get_data(filters):
|
||||
data = []
|
||||
document_type = filters.get("document_type")
|
||||
party = filters.get("document_id")
|
||||
filters = {
|
||||
"reference_doctype": document_type,
|
||||
"communication_type": "Feedback",
|
||||
"creation": ["Between", [filters.get("from_date"), filters.get("to_date")]]
|
||||
}
|
||||
fields = ["DATE_FORMAT(DATE(creation),'%m-%d-%Y')", "avg(rating) as rating"]
|
||||
|
||||
if not document_type:
|
||||
return []
|
||||
|
||||
if party:
|
||||
filters.update({ "reference_name": party })
|
||||
|
||||
party_details = frappe.get_list("Communication", filters=filters, fields=fields,
|
||||
order_by="creation", group_by="DATE_FORMAT(DATE(creation),'%m-%d-%Y')", as_list=True)
|
||||
|
||||
return party_details or []
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_document_type(doctype, txt, searchfield, start, page_len, filters):
|
||||
""" get the document type """
|
||||
|
||||
document_type = []
|
||||
txt = "%%%s%%" % txt
|
||||
|
||||
document_type = frappe.get_all("Feedback Trigger", filters={ "enabled": 1, "document_type": ("like", txt) },
|
||||
fields=["document_type"], as_list=True)
|
||||
|
||||
document_type = map(list, document_type)
|
||||
to_ignore = [ doc[0] for doc in document_type ]
|
||||
|
||||
documents = frappe.get_all("Feedback Request", filters={ "reference_doctype": ["not in", to_ignore] },
|
||||
fields=["reference_doctype"], distinct=True, as_list=True)
|
||||
|
||||
if documents:
|
||||
document_type.extend(documents)
|
||||
|
||||
return document_type
|
||||
|
|
@ -28,6 +28,11 @@ frappe.ui.form.on("Customize Form", {
|
|||
$(frm.wrapper).on("grid-make-sortable", function(e, frm) {
|
||||
frm.trigger("setup_sortable");
|
||||
});
|
||||
|
||||
$(frm.wrapper).on("grid-move-row", function(e, frm) {
|
||||
frm.trigger("setup_sortable");
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
doc_type: function(frm) {
|
||||
|
|
@ -57,12 +62,13 @@ frappe.ui.form.on("Customize Form", {
|
|||
frm.doc.fields.forEach(function(f, i) {
|
||||
var data_row = frm.page.body.find('[data-fieldname="fields"] [data-idx="'+ f.idx +'"] .data-row');
|
||||
|
||||
if(!f.is_custom_field) {
|
||||
data_row.removeClass('sortable-handle');
|
||||
} else {
|
||||
if(f.is_custom_field) {
|
||||
data_row.addClass("highlight");
|
||||
} else {
|
||||
f._sortable = false;
|
||||
}
|
||||
});
|
||||
frm.fields_dict.fields.grid.refresh();
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
|
|
|
|||
|
|
@ -1,694 +1,182 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"autoname": "DL.####",
|
||||
"beta": 0,
|
||||
"creation": "2013-01-29 17:55:08",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "Document",
|
||||
"editable_grid": 1,
|
||||
"autoname": "DL.####",
|
||||
"creation": "2013-01-29 17:55:08",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Document",
|
||||
"editable_grid": 1,
|
||||
"field_order": [
|
||||
"doc_type",
|
||||
"properties",
|
||||
"label",
|
||||
"default_print_format",
|
||||
"max_attachments",
|
||||
"allow_copy",
|
||||
"istable",
|
||||
"editable_grid",
|
||||
"quick_entry",
|
||||
"track_changes",
|
||||
"track_views",
|
||||
"image_view",
|
||||
"column_break_5",
|
||||
"title_field",
|
||||
"image_field",
|
||||
"search_fields",
|
||||
"section_break_8",
|
||||
"sort_field",
|
||||
"column_break_10",
|
||||
"sort_order",
|
||||
"fields_section_break",
|
||||
"fields"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "doc_type",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Enter Form Type",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "doc_type",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Enter Form Type",
|
||||
"options": "DocType"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "doc_type",
|
||||
"fieldname": "properties",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"depends_on": "doc_type",
|
||||
"fieldname": "properties",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "label",
|
||||
"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": "Change Label (via Custom Translation)",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "label",
|
||||
"fieldtype": "Data",
|
||||
"label": "Change Label (via Custom Translation)"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "default_print_format",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Default Print Format",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Print Format",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "default_print_format",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Default Print Format",
|
||||
"options": "Print Format"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "",
|
||||
"fieldname": "max_attachments",
|
||||
"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": "Max Attachments",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "max_attachments",
|
||||
"fieldtype": "Int",
|
||||
"label": "Max Attachments"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "allow_copy",
|
||||
"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": "Hide Copy",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "allow_copy",
|
||||
"fieldtype": "Check",
|
||||
"label": "Hide Copy"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "istable",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Is Table",
|
||||
"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
|
||||
},
|
||||
"fieldname": "istable",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Table",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "istable",
|
||||
"fieldname": "editable_grid",
|
||||
"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": "Editable Grid",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"depends_on": "istable",
|
||||
"fieldname": "editable_grid",
|
||||
"fieldtype": "Check",
|
||||
"label": "Editable Grid"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "1",
|
||||
"fieldname": "quick_entry",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Quick Entry",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"default": "1",
|
||||
"fieldname": "quick_entry",
|
||||
"fieldtype": "Check",
|
||||
"label": "Quick Entry"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "track_changes",
|
||||
"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": "Track Changes",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "track_changes",
|
||||
"fieldtype": "Check",
|
||||
"label": "Track Changes"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval: doc.image_field",
|
||||
"fieldname": "image_view",
|
||||
"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": "Image View",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"depends_on": "eval: doc.image_field",
|
||||
"fieldname": "image_view",
|
||||
"fieldtype": "Check",
|
||||
"label": "Image View"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_5",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "column_break_5",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "Use this fieldname to generate title",
|
||||
"fieldname": "title_field",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Title Field",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"description": "Use this fieldname to generate title",
|
||||
"fieldname": "title_field",
|
||||
"fieldtype": "Data",
|
||||
"label": "Title Field"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "Must be of type \"Attach Image\"",
|
||||
"fieldname": "image_field",
|
||||
"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": "Image Field",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"description": "Must be of type \"Attach Image\"",
|
||||
"fieldname": "image_field",
|
||||
"fieldtype": "Data",
|
||||
"label": "Image Field"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "Fields separated by comma (,) will be included in the \"Search By\" list of Search dialog box",
|
||||
"fieldname": "search_fields",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Search Fields",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"description": "Fields separated by comma (,) will be included in the \"Search By\" list of Search dialog box",
|
||||
"fieldname": "search_fields",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Search Fields"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "doc_type",
|
||||
"fieldname": "section_break_8",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"depends_on": "doc_type",
|
||||
"fieldname": "section_break_8",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "sort_field",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Sort Field",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "sort_field",
|
||||
"fieldtype": "Select",
|
||||
"label": "Sort Field"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_10",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "column_break_10",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "sort_order",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Sort Order",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "ASC\nDESC",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"fieldname": "sort_order",
|
||||
"fieldtype": "Select",
|
||||
"label": "Sort Order",
|
||||
"options": "ASC\nDESC"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "doc_type",
|
||||
"description": "Customize Label, Print Hide, Default etc.",
|
||||
"fieldname": "fields_section_break",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Fields",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
"depends_on": "doc_type",
|
||||
"description": "Customize Label, Print Hide, Default etc.",
|
||||
"fieldname": "fields_section_break",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Fields"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 1,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "fields",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Fields",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Customize Form Field",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
"allow_bulk_edit": 1,
|
||||
"fieldname": "fields",
|
||||
"fieldtype": "Table",
|
||||
"label": "Fields",
|
||||
"options": "Customize Form Field"
|
||||
},
|
||||
{
|
||||
"fieldname": "track_views",
|
||||
"fieldtype": "Check",
|
||||
"label": "Track Views"
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 1,
|
||||
"icon": "fa fa-glass",
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-04-21 16:59:12.752428",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Custom",
|
||||
"name": "Customize Form",
|
||||
"owner": "Administrator",
|
||||
],
|
||||
"hide_toolbar": 1,
|
||||
"icon": "fa fa-glass",
|
||||
"idx": 1,
|
||||
"issingle": 1,
|
||||
"modified": "2019-05-13 18:54:40.610862",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Custom",
|
||||
"name": "Customize Form",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 0,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"create": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"search_fields": "doc_type",
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"search_fields": "doc_type",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -28,6 +28,7 @@ doctype_properties = {
|
|||
'editable_grid': 'Check',
|
||||
'max_attachments': 'Int',
|
||||
'track_changes': 'Check',
|
||||
'track_views': 'Check',
|
||||
}
|
||||
|
||||
docfield_properties = {
|
||||
|
|
@ -87,6 +88,9 @@ class CustomizeForm(Document):
|
|||
if self.doc_type in core_doctypes_list:
|
||||
return frappe.msgprint(_("Core DocTypes cannot be customized."))
|
||||
|
||||
if meta.issingle:
|
||||
return frappe.msgprint(_("Single DocTypes cannot be customized."))
|
||||
|
||||
if meta.custom:
|
||||
return frappe.msgprint(_("Only standard DocTypes are allowed to be customized from Customize Form."))
|
||||
|
||||
|
|
@ -157,7 +161,7 @@ class CustomizeForm(Document):
|
|||
frappe.db.updatedb(self.doc_type)
|
||||
|
||||
if not hasattr(self, 'hide_success') or not self.hide_success:
|
||||
frappe.msgprint(_("{0} updated").format(_(self.doc_type)))
|
||||
frappe.msgprint(_("{0} updated").format(_(self.doc_type)), alert=True)
|
||||
frappe.clear_cache(doctype=self.doc_type)
|
||||
self.fetch_to_customize()
|
||||
|
||||
|
|
|
|||
|
|
@ -23,14 +23,14 @@ def drop_user_and_database(db_name, root_login=None, root_password=None):
|
|||
import frappe.database.mariadb.setup_db
|
||||
return frappe.database.mariadb.setup_db.drop_user_and_database(db_name, root_login, root_password)
|
||||
|
||||
def get_db(host=None, user=None, password=None):
|
||||
def get_db(host=None, user=None, password=None, port=None):
|
||||
import frappe
|
||||
if frappe.conf.db_type == 'postgres':
|
||||
import frappe.database.postgres.database
|
||||
return frappe.database.postgres.database.PostgresDatabase(host, user, password)
|
||||
return frappe.database.postgres.database.PostgresDatabase(host, user, password, port=port)
|
||||
else:
|
||||
import frappe.database.mariadb.database
|
||||
return frappe.database.mariadb.database.MariaDBDatabase(host, user, password)
|
||||
return frappe.database.mariadb.database.MariaDBDatabase(host, user, password, port=port)
|
||||
|
||||
def setup_help_database(help_db_name):
|
||||
import frappe
|
||||
|
|
@ -39,4 +39,4 @@ def setup_help_database(help_db_name):
|
|||
return frappe.database.postgres.setup_db.setup_help_database(help_db_name)
|
||||
else:
|
||||
import frappe.database.mariadb.setup_db
|
||||
return frappe.database.mariadb.setup_db.setup_help_database(help_db_name)
|
||||
return frappe.database.mariadb.setup_db.setup_help_database(help_db_name)
|
||||
|
|
|
|||
|
|
@ -46,9 +46,10 @@ class Database(object):
|
|||
class InvalidColumnName(frappe.ValidationError): pass
|
||||
|
||||
|
||||
def __init__(self, host=None, user=None, password=None, ac_name=None, use_default=0):
|
||||
def __init__(self, host=None, user=None, password=None, ac_name=None, use_default=0, port=None):
|
||||
self.setup_type_map()
|
||||
self.host = host or frappe.conf.db_host or 'localhost'
|
||||
self.port = port or frappe.conf.db_port or ''
|
||||
self.user = user or frappe.conf.db_name
|
||||
self.db_name = frappe.conf.db_name
|
||||
self._conn = None
|
||||
|
|
@ -153,6 +154,10 @@ class Database(object):
|
|||
frappe.log(values)
|
||||
frappe.log(">>>>")
|
||||
self._cursor.execute(query, values)
|
||||
|
||||
if frappe.flags.in_migrate:
|
||||
self.log_touched_tables(query, values)
|
||||
|
||||
else:
|
||||
if debug:
|
||||
if explain:
|
||||
|
|
@ -165,6 +170,9 @@ class Database(object):
|
|||
|
||||
self._cursor.execute(query)
|
||||
|
||||
if frappe.flags.in_migrate:
|
||||
self.log_touched_tables(query)
|
||||
|
||||
if debug:
|
||||
time_end = time()
|
||||
frappe.errprint(("Execution time: {0} sec").format(round(time_end - time_start, 2)))
|
||||
|
|
@ -173,6 +181,10 @@ class Database(object):
|
|||
if(frappe.conf.db_type == 'postgres'):
|
||||
self.rollback()
|
||||
|
||||
if frappe.conf.db_type == 'mariadb' and self.is_syntax_error(e):
|
||||
frappe.errprint('Syntax error in query:')
|
||||
frappe.errprint(query)
|
||||
|
||||
if ignore_ddl and (self.is_missing_column(e) or self.is_missing_table(e) or self.cant_drop_field_or_key(e)):
|
||||
pass
|
||||
else:
|
||||
|
|
@ -833,7 +845,7 @@ class Database(object):
|
|||
"""Returns list of column names from given doctype."""
|
||||
columns = self.get_db_table_columns('tab' + doctype)
|
||||
if not columns:
|
||||
raise self.ProgrammingError
|
||||
raise self.TableMissingError
|
||||
return columns
|
||||
|
||||
def has_column(self, doctype, column):
|
||||
|
|
@ -912,6 +924,20 @@ class Database(object):
|
|||
else:
|
||||
frappe.throw('No conditions provided')
|
||||
|
||||
def log_touched_tables(self, query, values=None):
|
||||
if values:
|
||||
query = frappe.safe_decode(self._cursor.mogrify(query, values))
|
||||
if query.strip().lower().split()[0] in ('insert', 'delete', 'update', 'alter'):
|
||||
# ([`\"']?) Captures ', " or ` at the begining of the table name (if provided)
|
||||
# (tab([A-Z]\w+)( [A-Z]\w+)*) Captures table names that start with "tab"
|
||||
# and are continued with multiple words that start with a captital letter
|
||||
# e.g. 'tabXxx' or 'tabXxx Xxx' or 'tabXxx Xxx Xxx' and so on
|
||||
# \1 matches the first captured group (quote character) at the end of the table name
|
||||
tables = [groups[1] for groups in re.findall(r'([`"\']?)(tab([A-Z]\w+)( [A-Z]\w+)*)\1', query)]
|
||||
if frappe.flags.touched_tables is None:
|
||||
frappe.flags.touched_tables = set()
|
||||
frappe.flags.touched_tables.update(tables)
|
||||
|
||||
|
||||
def enqueue_jobs_after_commit():
|
||||
if frappe.flags.enqueue_after_commit and len(frappe.flags.enqueue_after_commit) > 0:
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ from frappe.database.mariadb.schema import MariaDBTable
|
|||
|
||||
class MariaDBDatabase(Database):
|
||||
ProgrammingError = pymysql.err.ProgrammingError
|
||||
TableMissingError = pymysql.err.ProgrammingError
|
||||
OperationalError = pymysql.err.OperationalError
|
||||
InternalError = pymysql.err.InternalError
|
||||
SQLError = pymysql.err.ProgrammingError
|
||||
|
|
@ -80,11 +81,11 @@ class MariaDBDatabase(Database):
|
|||
|
||||
if usessl:
|
||||
conn = pymysql.connect(self.host, self.user or '', self.password or '',
|
||||
charset='utf8mb4', use_unicode = True, ssl=ssl_params,
|
||||
port=self.port, charset='utf8mb4', use_unicode = True, ssl=ssl_params,
|
||||
conv = conversions, local_infile = frappe.conf.local_infile)
|
||||
else:
|
||||
conn = pymysql.connect(self.host, self.user or '', self.password or '',
|
||||
charset='utf8mb4', use_unicode = True, conv = conversions,
|
||||
port=self.port, charset='utf8mb4', use_unicode = True, conv = conversions,
|
||||
local_infile = frappe.conf.local_infile)
|
||||
|
||||
# MYSQL_OPTION_MULTI_STATEMENTS_OFF = 1
|
||||
|
|
@ -163,6 +164,10 @@ class MariaDBDatabase(Database):
|
|||
def cant_drop_field_or_key(e):
|
||||
return e.args[0] == ER.CANT_DROP_FIELD_OR_KEY
|
||||
|
||||
@staticmethod
|
||||
def is_syntax_error(e):
|
||||
return e.args[0] == ER.PARSE_ERROR
|
||||
|
||||
def is_primary_key_violation(self, e):
|
||||
return self.is_duplicate_entry(e) and 'PRIMARY' in cstr(e.args[1])
|
||||
|
||||
|
|
@ -282,4 +287,4 @@ class MariaDBDatabase(Database):
|
|||
self.begin()
|
||||
|
||||
def get_database_list(self, target):
|
||||
return [d[0] for d in self.sql("SHOW DATABASES;")]
|
||||
return [d[0] for d in self.sql("SHOW DATABASES;")]
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ CREATE TABLE `tabDocField` (
|
|||
`unique` int(1) NOT NULL DEFAULT 0,
|
||||
`no_copy` int(1) NOT NULL DEFAULT 0,
|
||||
`allow_on_submit` int(1) NOT NULL DEFAULT 0,
|
||||
`show_preview_popup` int(1) NOT NULL DEFAULT 0,
|
||||
`trigger` varchar(255) DEFAULT NULL,
|
||||
`collapsible_depends_on` text,
|
||||
`depends_on` text,
|
||||
|
|
@ -49,6 +50,7 @@ CREATE TABLE `tabDocField` (
|
|||
`description` text,
|
||||
`in_list_view` int(1) NOT NULL DEFAULT 0,
|
||||
`in_standard_filter` int(1) NOT NULL DEFAULT 0,
|
||||
`in_preview` int(1) NOT NULL DEFAULT 0,
|
||||
`read_only` int(1) NOT NULL DEFAULT 0,
|
||||
`precision` varchar(255) DEFAULT NULL,
|
||||
`length` int(11) NOT NULL DEFAULT 0,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,32 @@ import frappe
|
|||
import os, sys
|
||||
from frappe.database.db_manager import DbManager
|
||||
|
||||
expected_settings_10_2_earlier = {
|
||||
"innodb_file_format": "Barracuda",
|
||||
"innodb_file_per_table": "ON",
|
||||
"innodb_large_prefix": "ON",
|
||||
"character_set_server": "utf8mb4",
|
||||
"collation_server": "utf8mb4_unicode_ci"
|
||||
}
|
||||
|
||||
expected_settings_10_3_later = {
|
||||
"character_set_server": "utf8mb4",
|
||||
"collation_server": "utf8mb4_unicode_ci"
|
||||
}
|
||||
|
||||
|
||||
def get_mariadb_versions():
|
||||
# MariaDB classifies their versions as Major (1st and 2nd number), and Minor (3rd number)
|
||||
# Example: Version 10.3.13 is Major Version = 10.3, Minor Version = 13
|
||||
mariadb_variables = frappe._dict(frappe.db.sql("""show variables"""))
|
||||
version_string = mariadb_variables.get('version').split('-')[0]
|
||||
versions = {}
|
||||
versions['major'] = version_string.split(
|
||||
'.')[0] + '.' + version_string.split('.')[1]
|
||||
versions['minor'] = version_string.split('.')[2]
|
||||
return versions
|
||||
|
||||
|
||||
def setup_database(force, source_sql, verbose):
|
||||
frappe.local.session = frappe._dict({'user':'Administrator'})
|
||||
|
||||
|
|
@ -54,7 +80,10 @@ def drop_user_and_database(db_name, root_login, root_password):
|
|||
|
||||
def bootstrap_database(db_name, verbose, source_sql=None):
|
||||
frappe.connect(db_name=db_name)
|
||||
check_if_ready_for_barracuda()
|
||||
if not check_database_settings():
|
||||
print('Database settings do not match expected values; stopping database setup.')
|
||||
sys.exit(1)
|
||||
|
||||
import_db_from_sql(source_sql, verbose)
|
||||
if not 'tabDefaultValue' in frappe.db.get_tables():
|
||||
print('''Database not installed, this can due to lack of permission, or that the database name exists.
|
||||
|
|
@ -69,38 +98,33 @@ def import_db_from_sql(source_sql=None, verbose=False):
|
|||
DbManager(frappe.local.db).restore_database(db_name, source_sql, db_name, frappe.conf.db_password)
|
||||
if verbose: print("Imported from database %s" % source_sql)
|
||||
|
||||
def check_if_ready_for_barracuda():
|
||||
|
||||
def check_database_settings():
|
||||
versions = get_mariadb_versions()
|
||||
if versions['major'] <= '10.2':
|
||||
expected_variables = expected_settings_10_2_earlier
|
||||
else:
|
||||
expected_variables = expected_settings_10_3_later
|
||||
|
||||
mariadb_variables = frappe._dict(frappe.db.sql("""show variables"""))
|
||||
mariadb_minor_version = int(mariadb_variables.get('version').split('-')[0].split('.')[1])
|
||||
if mariadb_minor_version < 3:
|
||||
check_database(mariadb_variables, {
|
||||
"innodb_file_format": "Barracuda",
|
||||
"innodb_file_per_table": "ON",
|
||||
"innodb_large_prefix": "ON"
|
||||
})
|
||||
check_database(mariadb_variables, {
|
||||
"character_set_server": "utf8mb4",
|
||||
"collation_server": "utf8mb4_unicode_ci"
|
||||
})
|
||||
# Check each expected value vs. actuals:
|
||||
result = True
|
||||
for key, expected_value in expected_variables.items():
|
||||
if mariadb_variables.get(key) != expected_value:
|
||||
print("For key %s. Expected value %s, found value %s" %
|
||||
(key, expected_value, mariadb_variables.get(key)))
|
||||
result = False
|
||||
if not result:
|
||||
site = frappe.local.site
|
||||
msg = ("Creation of your site - {x} failed because MariaDB is not properly {sep}"
|
||||
"configured. If using version 10.2.x or earlier, make sure you use the {sep}"
|
||||
"the Barracuda storage engine. {sep}{sep}"
|
||||
"Please verify the settings above in MariaDB's my.cnf. Restart MariaDB. And {sep}"
|
||||
"then run `bench new-site {x}` again.{sep2}"
|
||||
"").format(x=site, sep2="\n"*2, sep="\n")
|
||||
print_db_config(msg)
|
||||
return result
|
||||
|
||||
def check_database(mariadb_variables, variables_dict):
|
||||
mariadb_minor_version = int(mariadb_variables.get('version').split('-')[0].split('.')[1])
|
||||
for key, value in variables_dict.items():
|
||||
if mariadb_variables.get(key) != value:
|
||||
site = frappe.local.site
|
||||
msg = ("Creation of your site - {x} failed because MariaDB is not properly {sep}"
|
||||
"configured to use the Barracuda storage engine. {sep}"
|
||||
"Please add the settings below to MariaDB's my.cnf, restart MariaDB then {sep}"
|
||||
"run `bench new-site {x}` again.{sep2}"
|
||||
"").format(x=site, sep2="\n"*2, sep="\n")
|
||||
|
||||
if mariadb_minor_version < 3:
|
||||
print_db_config(msg, expected_config_for_barracuda_2)
|
||||
else:
|
||||
print_db_config(msg, expected_config_for_barracuda_3)
|
||||
raise frappe.exceptions.ImproperDBConfigurationError(
|
||||
reason="MariaDB default file format is not Barracuda"
|
||||
)
|
||||
|
||||
def get_root_connection(root_login, root_password):
|
||||
import getpass
|
||||
|
|
@ -118,31 +142,8 @@ def get_root_connection(root_login, root_password):
|
|||
|
||||
return frappe.local.flags.root_connection
|
||||
|
||||
def print_db_config(explanation, config_text):
|
||||
|
||||
def print_db_config(explanation):
|
||||
print("="*80)
|
||||
print(explanation)
|
||||
print(config_text)
|
||||
print("="*80)
|
||||
|
||||
expected_config_for_barracuda_2 = """
|
||||
[mysqld]
|
||||
innodb-file-format=barracuda
|
||||
innodb-file-per-table=1
|
||||
innodb-large-prefix=1
|
||||
character-set-client-handshake = FALSE
|
||||
character-set-server = utf8mb4
|
||||
collation-server = utf8mb4_unicode_ci
|
||||
|
||||
[mysql]
|
||||
default-character-set = utf8mb4
|
||||
"""
|
||||
|
||||
expected_config_for_barracuda_3 = """
|
||||
[mysqld]
|
||||
character-set-client-handshake = FALSE
|
||||
character-set-server = utf8mb4
|
||||
collation-server = utf8mb4_unicode_ci
|
||||
|
||||
[mysql]
|
||||
default-character-set = utf8mb4
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ psycopg2.extensions.register_type(DEC2FLOAT)
|
|||
|
||||
class PostgresDatabase(Database):
|
||||
ProgrammingError = psycopg2.ProgrammingError
|
||||
TableMissingError = psycopg2.ProgrammingError
|
||||
OperationalError = psycopg2.OperationalError
|
||||
InternalError = psycopg2.InternalError
|
||||
SQLError = psycopg2.ProgrammingError
|
||||
|
|
@ -63,10 +64,10 @@ class PostgresDatabase(Database):
|
|||
|
||||
def get_connection(self):
|
||||
# warnings.filterwarnings('ignore', category=psycopg2.Warning)
|
||||
conn = psycopg2.connect('host={} dbname={}'.format(self.host, self.user))
|
||||
conn = psycopg2.connect('host={} dbname={} user={} password={} port={}'.format(
|
||||
self.host, self.user, self.user, self.password, self.port
|
||||
))
|
||||
conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT) # TODO: Remove this
|
||||
# conn = psycopg2.connect('host={} dbname={} user={} password={}'.format(self.host,
|
||||
# self.user, self.user, self.password))
|
||||
|
||||
return conn
|
||||
|
||||
|
|
@ -309,4 +310,4 @@ def replace_locate_with_strpos(query):
|
|||
# strpos is the locate equivalent in postgres
|
||||
if re.search(r'locate\(', query, flags=re.IGNORECASE):
|
||||
query = re.sub(r'locate\(([^,]+),([^)]+)\)', r'strpos(\2, \1)', query, flags=re.IGNORECASE)
|
||||
return query
|
||||
return query
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ CREATE TABLE "tabDocField" (
|
|||
"unique" smallint NOT NULL DEFAULT 0,
|
||||
"no_copy" smallint NOT NULL DEFAULT 0,
|
||||
"allow_on_submit" smallint NOT NULL DEFAULT 0,
|
||||
"show_preview_popup" smallint NOT NULL DEFAULT 0,
|
||||
"trigger" varchar(255) DEFAULT NULL,
|
||||
"collapsible_depends_on" text,
|
||||
"depends_on" text,
|
||||
|
|
@ -49,6 +50,7 @@ CREATE TABLE "tabDocField" (
|
|||
"description" text,
|
||||
"in_list_view" smallint NOT NULL DEFAULT 0,
|
||||
"in_standard_filter" smallint NOT NULL DEFAULT 0,
|
||||
"in_preview" smallint NOT NULL DEFAULT 0,
|
||||
"read_only" smallint NOT NULL DEFAULT 0,
|
||||
"precision" varchar(255) DEFAULT NULL,
|
||||
"length" bigint NOT NULL DEFAULT 0,
|
||||
|
|
|
|||
|
|
@ -40,7 +40,20 @@ class PostgresTable(DBTable):
|
|||
query.append("ADD COLUMN `{}` {}".format(col.fieldname, col.get_definition()))
|
||||
|
||||
for col in self.change_type:
|
||||
query.append("ALTER COLUMN `{}` TYPE {}".format(col.fieldname, get_definition(col.fieldtype, precision=col.precision, length=col.length)))
|
||||
using_clause = ""
|
||||
if col.fieldtype in ("Datetime"):
|
||||
# The USING option of SET DATA TYPE can actually specify any expression
|
||||
# involving the old values of the row
|
||||
# read more https://www.postgresql.org/docs/9.1/sql-altertable.html
|
||||
using_clause = "USING {}::timestamp without time zone".format(col.fieldname)
|
||||
elif col.fieldtype in ("Check"):
|
||||
using_clause = "USING {}::smallint".format(col.fieldname)
|
||||
|
||||
query.append("ALTER COLUMN {0} TYPE {1} {2}".format(
|
||||
col.fieldname,
|
||||
get_definition(col.fieldtype, precision=col.precision, length=col.length),
|
||||
using_clause)
|
||||
)
|
||||
|
||||
for col in self.set_default:
|
||||
if col.fieldname=="name":
|
||||
|
|
@ -93,4 +106,4 @@ class PostgresTable(DBTable):
|
|||
fieldname, self.table_name)))
|
||||
raise e
|
||||
else:
|
||||
raise e
|
||||
raise e
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import frappe, subprocess, os
|
||||
from six.moves import input
|
||||
|
||||
def setup_database(force, source_sql, verbose):
|
||||
root_conn = get_root_connection()
|
||||
|
|
@ -10,9 +11,16 @@ def setup_database(force, source_sql, verbose):
|
|||
frappe.conf.db_password))
|
||||
root_conn.sql("GRANT ALL PRIVILEGES ON DATABASE `{0}` TO {0}".format(frappe.conf.db_name))
|
||||
|
||||
# we can't pass psql password in arguments in postgresql as mysql. So
|
||||
# set password connection parameter in environment variable
|
||||
subprocess_env = os.environ.copy()
|
||||
subprocess_env['PGPASSWORD'] = str(frappe.conf.db_password)
|
||||
# bootstrap db
|
||||
subprocess.check_output(['psql', frappe.conf.db_name, '-qf',
|
||||
os.path.join(os.path.dirname(__file__), 'framework_postgres.sql')])
|
||||
subprocess.check_output([
|
||||
'psql', frappe.conf.db_name, '-h', 'localhost', '-U',
|
||||
frappe.conf.db_name, '-f',
|
||||
os.path.join(os.path.dirname(__file__), 'framework_postgres.sql')
|
||||
], env=subprocess_env)
|
||||
|
||||
frappe.connect()
|
||||
|
||||
|
|
@ -24,17 +32,20 @@ def setup_help_database(help_db_name):
|
|||
root_conn.sql("CREATE user {0} password '{1}'".format(help_db_name, help_db_name))
|
||||
root_conn.sql("GRANT ALL PRIVILEGES ON DATABASE `{0}` TO {0}".format(help_db_name))
|
||||
|
||||
def get_root_connection(root_login='postgres', root_password=None):
|
||||
def get_root_connection(root_login=None, root_password=None):
|
||||
import getpass
|
||||
if not frappe.local.flags.root_connection:
|
||||
if not root_login:
|
||||
root_login = 'root'
|
||||
root_login = frappe.conf.get("root_login") or None
|
||||
|
||||
if not root_login:
|
||||
root_login = input("Enter postgres super user: ")
|
||||
|
||||
if not root_password:
|
||||
root_password = frappe.conf.get("root_password") or None
|
||||
|
||||
if not root_password:
|
||||
root_password = getpass.getpass("Postgres root password: ")
|
||||
root_password = getpass.getpass("Postgres super user password: ")
|
||||
|
||||
frappe.local.flags.root_connection = frappe.database.get_db(user=root_login, password=root_password)
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ frappe.ui.form.on('Dashboard Chart', {
|
|||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
frm.filters = null;
|
||||
frm.chart_filters = null;
|
||||
frm.set_df_property("filters_section", "hidden", 1);
|
||||
frm.trigger('update_options');
|
||||
},
|
||||
|
|
@ -58,7 +58,7 @@ frappe.ui.form.on('Dashboard Chart', {
|
|||
if (['Date', 'Datetime'].includes(df.fieldtype)) {
|
||||
date_fields.push({label: df.label, value: df.fieldname});
|
||||
}
|
||||
if (['In', 'Float', 'Currency', 'Percent'].includes(df.fieldtype)) {
|
||||
if (['Int', 'Float', 'Currency', 'Percent'].includes(df.fieldtype)) {
|
||||
value_fields.push({label: df.label, value: df.fieldname});
|
||||
}
|
||||
});
|
||||
|
|
@ -72,21 +72,26 @@ frappe.ui.form.on('Dashboard Chart', {
|
|||
},
|
||||
|
||||
show_filters: function(frm) {
|
||||
if (frm.filters) {
|
||||
if (frm.chart_filters && frm.chart_filters.length) {
|
||||
frm.trigger('render_filters_table');
|
||||
} else {
|
||||
if (frm.doc.chart_type==='Custom') {
|
||||
frappe.xcall('frappe.desk.doctype.dashboard_chart_source.dashboard_chart_source.get_config', {name: frm.doc.source})
|
||||
.then(config => {
|
||||
frappe.dom.eval(config);
|
||||
frm.filters = frappe.dashboards.chart_sources[frm.doc.source].filters;
|
||||
frm.trigger('render_filters_table');
|
||||
});
|
||||
if (frm.doc.source) {
|
||||
frappe.xcall('frappe.desk.doctype.dashboard_chart_source.dashboard_chart_source.get_config', {name: frm.doc.source})
|
||||
.then(config => {
|
||||
frappe.dom.eval(config);
|
||||
frm.chart_filters = frappe.dashboards.chart_sources[frm.doc.source].filters;
|
||||
frm.trigger('render_filters_table');
|
||||
});
|
||||
} else {
|
||||
frm.chart_filters = [];
|
||||
frm.trigger('render_filters_table');
|
||||
}
|
||||
} else {
|
||||
// standard filters
|
||||
if (frm.doc.document_type) {
|
||||
// allow all link and select fields as filters
|
||||
frm.filters = [];
|
||||
frm.chart_filters = [];
|
||||
frappe.model.with_doctype(frm.doc.document_type, () => {
|
||||
frappe.get_meta(frm.doc.document_type).fields.map(df => {
|
||||
if (['Link', 'Select'].includes(df.fieldtype)) {
|
||||
|
|
@ -95,12 +100,11 @@ frappe.ui.form.on('Dashboard Chart', {
|
|||
// nothing is mandatory
|
||||
_df.reqd = 0;
|
||||
_df.default = null;
|
||||
_df.read_only = 0;
|
||||
_df.permlevel = 1;
|
||||
_df.hidden = 0;
|
||||
|
||||
// no default
|
||||
|
||||
if (!df.read_only && !df.hidden) {
|
||||
frm.filters.push(_df);
|
||||
}
|
||||
frm.chart_filters.push(_df);
|
||||
}
|
||||
frm.trigger('render_filters_table');
|
||||
});
|
||||
|
|
@ -113,7 +117,7 @@ frappe.ui.form.on('Dashboard Chart', {
|
|||
|
||||
render_filters_table: function(frm) {
|
||||
frm.set_df_property("filters_section", "hidden", 0);
|
||||
let fields = frm.filters;
|
||||
let fields = frm.chart_filters;
|
||||
|
||||
let wrapper = $(frm.get_field('filters_json').wrapper).empty();
|
||||
let table = $(`<table class="table table-bordered" style="cursor:pointer; margin:0px;">
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@ from frappe.utils import getdate
|
|||
from frappe.desk.doctype.dashboard_chart.dashboard_chart import (get,
|
||||
get_period_ending)
|
||||
|
||||
from datetime import datetime
|
||||
from dateutil.relativedelta import relativedelta
|
||||
import calendar
|
||||
|
||||
class TestDashboardChart(unittest.TestCase):
|
||||
def test_period_ending(self):
|
||||
self.assertEqual(get_period_ending('2019-04-10', 'Daily'),
|
||||
|
|
@ -48,21 +52,13 @@ class TestDashboardChart(unittest.TestCase):
|
|||
timeseries = 1
|
||||
)).insert()
|
||||
|
||||
result = get(chart_name ='Test Dashboard Chart',
|
||||
to_date = '2019-04-11', refresh = 1)
|
||||
self.assertEqual(result.get('labels')[0], '2018-04-30')
|
||||
self.assertEqual(result.get('labels')[1], '2018-05-31')
|
||||
self.assertEqual(result.get('labels')[2], '2018-06-30')
|
||||
self.assertEqual(result.get('labels')[3], '2018-07-31')
|
||||
self.assertEqual(result.get('labels')[4], '2018-08-31')
|
||||
self.assertEqual(result.get('labels')[5], '2018-09-30')
|
||||
self.assertEqual(result.get('labels')[6], '2018-10-31')
|
||||
self.assertEqual(result.get('labels')[7], '2018-11-30')
|
||||
self.assertEqual(result.get('labels')[8], '2018-12-31')
|
||||
self.assertEqual(result.get('labels')[9], '2019-01-31')
|
||||
self.assertEqual(result.get('labels')[10], '2019-02-28')
|
||||
self.assertEqual(result.get('labels')[11], '2019-03-31')
|
||||
self.assertEqual(result.get('labels')[12], '2019-04-30')
|
||||
cur_date = datetime.now() - relativedelta(years=1)
|
||||
|
||||
result = get(chart_name ='Test Dashboard Chart', refresh = 1)
|
||||
for idx in range(13):
|
||||
month = str(cur_date.year) + '-' + str(cur_date.strftime('%m')) + '-' + str(calendar.monthrange(cur_date.year, cur_date.month)[1])
|
||||
self.assertEqual(result.get('labels')[idx], month)
|
||||
cur_date += relativedelta(months=1)
|
||||
|
||||
# self.assertEqual(result.get('datasets')[0].get('values')[:-1],
|
||||
# [44, 28, 8, 11, 2, 6, 18, 6, 4, 5, 15, 13])
|
||||
|
|
@ -87,21 +83,13 @@ class TestDashboardChart(unittest.TestCase):
|
|||
timeseries = 1
|
||||
)).insert()
|
||||
|
||||
result = get(chart_name ='Test Empty Dashboard Chart',
|
||||
to_date = '2019-04-11', refresh = 1)
|
||||
self.assertEqual(result.get('labels')[0], '2018-04-30')
|
||||
self.assertEqual(result.get('labels')[1], '2018-05-31')
|
||||
self.assertEqual(result.get('labels')[2], '2018-06-30')
|
||||
self.assertEqual(result.get('labels')[3], '2018-07-31')
|
||||
self.assertEqual(result.get('labels')[4], '2018-08-31')
|
||||
self.assertEqual(result.get('labels')[5], '2018-09-30')
|
||||
self.assertEqual(result.get('labels')[6], '2018-10-31')
|
||||
self.assertEqual(result.get('labels')[7], '2018-11-30')
|
||||
self.assertEqual(result.get('labels')[8], '2018-12-31')
|
||||
self.assertEqual(result.get('labels')[9], '2019-01-31')
|
||||
self.assertEqual(result.get('labels')[10], '2019-02-28')
|
||||
self.assertEqual(result.get('labels')[11], '2019-03-31')
|
||||
self.assertEqual(result.get('labels')[12], '2019-04-30')
|
||||
cur_date = datetime.now() - relativedelta(years=1)
|
||||
|
||||
result = get(chart_name ='Test Empty Dashboard Chart', refresh = 1)
|
||||
for idx in range(13):
|
||||
month = str(cur_date.year) + '-' + str(cur_date.strftime('%m')) + '-' + str(calendar.monthrange(cur_date.year, cur_date.month)[1])
|
||||
self.assertEqual(result.get('labels')[idx], month)
|
||||
cur_date += relativedelta(months=1)
|
||||
|
||||
frappe.db.rollback()
|
||||
|
||||
|
|
@ -126,24 +114,16 @@ class TestDashboardChart(unittest.TestCase):
|
|||
timeseries = 1
|
||||
)).insert()
|
||||
|
||||
result = get(chart_name ='Test Empty Dashboard Chart 2',
|
||||
to_date = '2019-04-11', refresh = 1)
|
||||
self.assertEqual(result.get('labels')[0], '2018-04-30')
|
||||
self.assertEqual(result.get('labels')[1], '2018-05-31')
|
||||
self.assertEqual(result.get('labels')[2], '2018-06-30')
|
||||
self.assertEqual(result.get('labels')[3], '2018-07-31')
|
||||
self.assertEqual(result.get('labels')[4], '2018-08-31')
|
||||
self.assertEqual(result.get('labels')[5], '2018-09-30')
|
||||
self.assertEqual(result.get('labels')[6], '2018-10-31')
|
||||
self.assertEqual(result.get('labels')[7], '2018-11-30')
|
||||
self.assertEqual(result.get('labels')[8], '2018-12-31')
|
||||
self.assertEqual(result.get('labels')[9], '2019-01-31')
|
||||
self.assertEqual(result.get('labels')[10], '2019-02-28')
|
||||
self.assertEqual(result.get('labels')[11], '2019-03-31')
|
||||
self.assertEqual(result.get('labels')[12], '2019-04-30')
|
||||
cur_date = datetime.now() - relativedelta(years=1)
|
||||
|
||||
result = get(chart_name ='Test Empty Dashboard Chart 2', refresh = 1)
|
||||
for idx in range(13):
|
||||
month = str(cur_date.year) + '-' + str(cur_date.strftime('%m')) + '-' + str(calendar.monthrange(cur_date.year, cur_date.month)[1])
|
||||
self.assertEqual(result.get('labels')[idx], month)
|
||||
cur_date += relativedelta(months=1)
|
||||
|
||||
# only 1 data point with value
|
||||
self.assertEqual(result.get('datasets')[0].get('values')[2], 1)
|
||||
self.assertEqual(result.get('datasets')[0].get('values')[2], 0)
|
||||
|
||||
frappe.db.rollback()
|
||||
|
||||
|
|
|
|||
|
|
@ -43,10 +43,17 @@ class Event(Document):
|
|||
def sync_communication(self):
|
||||
if self.event_participants:
|
||||
for participant in self.event_participants:
|
||||
communication_name = frappe.db.get_value("Communication", dict(reference_doctype=self.doctype, reference_name=self.name, timeline_doctype=participant.reference_doctype, timeline_name=participant.reference_docname), "name")
|
||||
if communication_name:
|
||||
communication = frappe.get_doc("Communication", communication_name)
|
||||
self.update_communication(participant, communication)
|
||||
comms = frappe.get_list("Communication", filters=[
|
||||
["Communication", "reference_doctype", "=", self.doctype],
|
||||
["Communication", "reference_name", "=", self.name],
|
||||
["Dynamic Link", "link_doctype", "=", participant.reference_doctype],
|
||||
["Dynamic Link", "link_name", "=", participant.reference_docname]
|
||||
], fields=["name"])
|
||||
|
||||
if comms:
|
||||
for comm in comms:
|
||||
communication = frappe.get_doc("Communication", comm.name)
|
||||
self.update_communication(participant, communication)
|
||||
else:
|
||||
meta = frappe.get_meta(participant.reference_doctype)
|
||||
if hasattr(meta, "allow_events_in_timeline") and meta.allow_events_in_timeline==1:
|
||||
|
|
@ -62,12 +69,11 @@ class Event(Document):
|
|||
communication.subject = self.subject
|
||||
communication.content = self.description if self.description else self.subject
|
||||
communication.communication_date = self.starts_on
|
||||
communication.timeline_doctype = participant.reference_doctype
|
||||
communication.timeline_name = participant.reference_docname
|
||||
communication.reference_doctype = self.doctype
|
||||
communication.reference_name = self.name
|
||||
communication.communication_medium = communication_mapping[self.event_category] if self.event_category else ""
|
||||
communication.status = "Linked"
|
||||
communication.add_link(participant.reference_doctype, participant.reference_docname)
|
||||
communication.save(ignore_permissions=True)
|
||||
|
||||
@frappe.whitelist()
|
||||
|
|
@ -76,9 +82,18 @@ def delete_communication(event, reference_doctype, reference_docname):
|
|||
if isinstance(event, string_types):
|
||||
event = json.loads(event)
|
||||
|
||||
communication_name = frappe.db.get_value("Communication", dict(reference_doctype=event["doctype"], reference_name=event["name"], timeline_doctype=deleted_participant.reference_doctype, timeline_name=deleted_participant.reference_docname), "name")
|
||||
if communication_name:
|
||||
deletion = frappe.get_doc("Communication", communication_name).delete()
|
||||
comms = frappe.get_list("Communication", filters=[
|
||||
["Communication", "reference_doctype", "=", event.get("doctype")],
|
||||
["Communication", "reference_name", "=", event.get("name")],
|
||||
["Dynamic Link", "link_doctype", "=", deleted_participant.reference_doctype],
|
||||
["Dynamic Link", "link_name", "=", deleted_participant.reference_docname]
|
||||
], fields=["name"])
|
||||
|
||||
if comms:
|
||||
deletion = []
|
||||
for comm in comms:
|
||||
delete = frappe.get_doc("Communication", comm.name).delete()
|
||||
deletion.append(delete)
|
||||
|
||||
return deletion
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "description_and_status",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
|
|
@ -53,6 +54,7 @@
|
|||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Open",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
|
|
@ -86,6 +88,7 @@
|
|||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Medium",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "priority",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
|
|
@ -120,6 +123,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
|
|
@ -150,6 +154,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "color",
|
||||
"fieldtype": "Color",
|
||||
"hidden": 0,
|
||||
|
|
@ -157,7 +162,7 @@
|
|||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Color",
|
||||
"length": 0,
|
||||
|
|
@ -182,6 +187,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
|
|
@ -189,7 +195,7 @@
|
|||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Due Date",
|
||||
"length": 0,
|
||||
|
|
@ -215,6 +221,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "owner",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
|
|
@ -247,6 +254,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "description_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
|
|
@ -279,6 +287,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Text Editor",
|
||||
"hidden": 0,
|
||||
|
|
@ -314,6 +323,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "section_break_6",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
|
|
@ -345,6 +355,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "reference_type",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
|
|
@ -352,7 +363,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": "Reference Type",
|
||||
"length": 0,
|
||||
|
|
@ -379,6 +390,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "reference_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
|
|
@ -413,6 +425,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_10",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
|
|
@ -443,6 +456,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "role",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
|
|
@ -477,6 +491,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "assigned_by",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
|
|
@ -510,6 +525,7 @@
|
|||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "assigned_by.full_name",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "assigned_by_full_name",
|
||||
"fieldtype": "Read Only",
|
||||
"hidden": 0,
|
||||
|
|
@ -543,6 +559,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "sender",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
|
|
@ -575,6 +592,7 @@
|
|||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "assignment_rule",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
|
|
@ -603,17 +621,16 @@
|
|||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "fa fa-check",
|
||||
"idx": 2,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-03-07 16:11:25.764549",
|
||||
"menu_index": 0,
|
||||
"modified": "2019-04-24 15:45:23.290491",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "ToDo",
|
||||
|
|
@ -660,7 +677,6 @@
|
|||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"search_fields": "description, reference_type, reference_name",
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_order": "DESC",
|
||||
|
|
|
|||
|
|
@ -43,8 +43,11 @@ class ToDo(Document):
|
|||
|
||||
def on_trash(self):
|
||||
# unlink todo from linked comments
|
||||
frappe.db.sql("""update `tabCommunication` set link_doctype=null, link_name=null
|
||||
where link_doctype=%(doctype)s and link_name=%(name)s""", {"doctype": self.doctype, "name": self.name})
|
||||
frappe.db.sql("""
|
||||
delete from `tabDynamic Link`
|
||||
where link_doctype=%(doctype)s and link_name=%(name)s""", {
|
||||
"doctype": self.doctype, "name": self.name
|
||||
})
|
||||
|
||||
self.update_in_reference()
|
||||
|
||||
|
|
@ -94,7 +97,7 @@ def get_permission_query_conditions(user):
|
|||
if "System Manager" in frappe.get_roles(user):
|
||||
return None
|
||||
else:
|
||||
return """(tabToDo.owner = {user} or tabToDo.assigned_by = {user})"""\
|
||||
return """(`tabToDo`.owner = {user} or `tabToDo`.assigned_by = {user})"""\
|
||||
.format(user=frappe.db.escape(user))
|
||||
|
||||
def has_permission(doc, user):
|
||||
|
|
@ -108,4 +111,4 @@ def new_todo(description):
|
|||
frappe.get_doc({
|
||||
'doctype': 'ToDo',
|
||||
'description': description
|
||||
}).insert()
|
||||
}).insert()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
frappe.listview_settings['ToDo'] = {
|
||||
hide_name_column: true,
|
||||
add_fields: ["reference_type", "reference_name"],
|
||||
|
||||
onload: function(me) {
|
||||
if (!frappe.route_options) {
|
||||
frappe.route_options = {
|
||||
|
|
@ -8,7 +11,22 @@ frappe.listview_settings['ToDo'] = {
|
|||
}
|
||||
me.page.set_title(__("To Do"));
|
||||
},
|
||||
hide_name_column: true,
|
||||
|
||||
button: {
|
||||
show: function(doc) {
|
||||
return doc.reference_name;
|
||||
},
|
||||
get_label: function() {
|
||||
return __('Open');
|
||||
},
|
||||
get_description: function(doc) {
|
||||
return __('Open {0}', [`${doc.reference_type} ${doc.reference_name}`])
|
||||
},
|
||||
action: function(doc) {
|
||||
frappe.set_route('Form', doc.reference_type, doc.reference_name);
|
||||
}
|
||||
},
|
||||
|
||||
refresh: function(me) {
|
||||
if (me.todo_sidebar_setup) return;
|
||||
|
||||
|
|
@ -19,5 +37,4 @@ frappe.listview_settings['ToDo'] = {
|
|||
|
||||
me.todo_sidebar_setup = true;
|
||||
},
|
||||
add_fields: ["reference_type", "reference_name"],
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ from __future__ import unicode_literals
|
|||
import frappe
|
||||
from frappe import _
|
||||
from frappe.desk.form.document_follow import follow_document
|
||||
from frappe.utils import cint
|
||||
import frappe.share
|
||||
|
||||
class DuplicateToDoError(frappe.ValidationError): pass
|
||||
|
|
@ -177,7 +178,7 @@ def notify_assignment(assigned_by, owner, doc_type, doc_name, action='CLOSE',
|
|||
'notify': notify
|
||||
}
|
||||
|
||||
if arg and arg.get("notify"):
|
||||
if arg and cint(arg.get("notify")):
|
||||
_notify(arg)
|
||||
|
||||
def _notify(args):
|
||||
|
|
|
|||
|
|
@ -54,8 +54,8 @@ def unfollow_document(doctype, doc_name, user):
|
|||
return 1
|
||||
return 0
|
||||
|
||||
def get_message(doc_name, doctype, frequency):
|
||||
activity_list = get_version(doctype, doc_name, frequency) + get_comments(doctype, doc_name, frequency)
|
||||
def get_message(doc_name, doctype, frequency, user):
|
||||
activity_list = get_version(doctype, doc_name, frequency, user) + get_comments(doctype, doc_name, frequency, user)
|
||||
return sorted(activity_list, key=lambda k: k["time"], reverse=True)
|
||||
|
||||
def send_email_alert(receiver, docinfo, timeline):
|
||||
|
|
@ -98,7 +98,7 @@ def send_document_follow_mails(frequency):
|
|||
valid_document_follows = []
|
||||
if user_frequency == frequency:
|
||||
for d in grouped_by_user[user]:
|
||||
content = get_message(d.ref_docname, d.ref_doctype, frequency)
|
||||
content = get_message(d.ref_docname, d.ref_doctype, frequency, user)
|
||||
if content:
|
||||
message = message + content
|
||||
valid_document_follows.append({
|
||||
|
|
@ -107,13 +107,13 @@ def send_document_follow_mails(frequency):
|
|||
"reference_url": get_url_to_form(d.ref_doctype, d.ref_docname)
|
||||
})
|
||||
|
||||
if message:
|
||||
if message and frappe.db.get_value("User", user, "document_follow_notify", ignore=True):
|
||||
send_email_alert(user, valid_document_follows, message)
|
||||
|
||||
|
||||
def get_version(doctype, doc_name, frequency):
|
||||
def get_version(doctype, doc_name, frequency, user):
|
||||
timeline = []
|
||||
filters = get_filters("docname", doc_name, frequency)
|
||||
filters = get_filters("docname", doc_name, frequency, user)
|
||||
version = frappe.get_all("Version",
|
||||
filters=filters,
|
||||
fields=["ref_doctype", "data", "modified", "modified", "modified_by"]
|
||||
|
|
@ -134,9 +134,9 @@ def get_version(doctype, doc_name, frequency):
|
|||
|
||||
return timeline
|
||||
|
||||
def get_comments(doctype, doc_name, frequency):
|
||||
def get_comments(doctype, doc_name, frequency, user):
|
||||
timeline = []
|
||||
filters = get_filters("reference_name", doc_name, frequency)
|
||||
filters = get_filters("reference_name", doc_name, frequency, user)
|
||||
comments = frappe.get_all("Comment",
|
||||
filters=filters,
|
||||
fields=["content", "modified", "modified_by", "comment_type"]
|
||||
|
|
@ -255,26 +255,29 @@ def send_daily_updates():
|
|||
def send_weekly_updates():
|
||||
send_document_follow_mails("Weekly")
|
||||
|
||||
def get_filters(search_by, name, frequency):
|
||||
def get_filters(search_by, name, frequency, user):
|
||||
filters = []
|
||||
|
||||
if frequency == "Weekly":
|
||||
filters = [
|
||||
[search_by, "=", name],
|
||||
["modified", ">", frappe.utils.add_days(frappe.utils.nowdate(),-7)],
|
||||
["modified", "<", frappe.utils.nowdate()]
|
||||
["modified", "<", frappe.utils.nowdate()],
|
||||
["modified_by", "!=", user]
|
||||
]
|
||||
elif frequency == "Daily":
|
||||
filters = [
|
||||
[search_by, "=", name],
|
||||
["modified", ">", frappe.utils.add_days(frappe.utils.nowdate(),-1)],
|
||||
["modified", "<", frappe.utils.nowdate()]
|
||||
["modified", "<", frappe.utils.nowdate()],
|
||||
["modified_by", "!=", user]
|
||||
]
|
||||
elif frequency == "Hourly":
|
||||
filters = [
|
||||
[search_by, "=", name],
|
||||
["modified", ">", frappe.utils.add_to_date(frappe.utils.now_datetime(), 0, 0, 0, -1)],
|
||||
["modified", "<", frappe.utils.now_datetime()]
|
||||
["modified", ">", frappe.utils.add_to_date(frappe.utils.now_datetime(), hours=-1)],
|
||||
["modified", "<", frappe.utils.now_datetime()],
|
||||
["modified_by", "!=", user]
|
||||
]
|
||||
|
||||
return filters
|
||||
|
|
|
|||
|
|
@ -47,9 +47,6 @@ def getdoc(doctype, name, user=None):
|
|||
frappe.errprint(frappe.utils.get_traceback())
|
||||
raise
|
||||
|
||||
if doc and not name.startswith('_'):
|
||||
frappe.get_user().update_recent(doctype, name)
|
||||
|
||||
doc.add_seen()
|
||||
|
||||
frappe.response.docs.append(doc)
|
||||
|
|
@ -100,13 +97,16 @@ def get_docinfo(doc=None, doctype=None, name=None):
|
|||
"assignments": get_assignments(doc.doctype, doc.name),
|
||||
"permissions": get_doc_permissions(doc),
|
||||
"shared": frappe.share.get_users(doc.doctype, doc.name),
|
||||
"rating": get_feedback_rating(doc.doctype, doc.name),
|
||||
"views": get_view_logs(doc.doctype, doc.name),
|
||||
"energy_point_logs": get_point_logs(doc.doctype, doc.name),
|
||||
"is_document_followed": is_document_followed(doc.doctype, doc.name, frappe.session.user),
|
||||
"document_follow_enabled": frappe.db.get_value("User", frappe.session.user, "document_follow_notify")
|
||||
"milestones": get_milestones(doc.doctype, doc.name),
|
||||
"is_document_followed": is_document_followed(doc.doctype, doc.name, frappe.session.user)
|
||||
}
|
||||
|
||||
def get_milestones(doctype, name):
|
||||
return frappe.db.get_all('Milestone', fields = ['creation', 'owner', 'track_field', 'value'],
|
||||
filters=dict(reference_type=doctype, reference_name=name))
|
||||
|
||||
def get_attachments(dt, dn):
|
||||
return frappe.get_all("File", fields=["name", "file_name", "file_url", "is_private"],
|
||||
filters = {"attached_to_name": dn, "attached_to_doctype": dt})
|
||||
|
|
@ -160,36 +160,50 @@ def get_communication_data(doctype, name, start=0, limit=20, after=None, fields=
|
|||
group_by=None, as_dict=True):
|
||||
'''Returns list of communications for a given document'''
|
||||
if not fields:
|
||||
fields = '''`name`, `communication_type`,`communication_medium`, `comment_type`,
|
||||
`communication_date`, `content`, `sender`, `sender_full_name`,
|
||||
`creation`, `subject`, `delivery_status`, `_liked_by`,
|
||||
`timeline_doctype`, `timeline_name`, `reference_doctype`, `reference_name`,
|
||||
`link_doctype`, `link_name`, `read_by_recipient`, `rating`, 'Communication' AS `doctype`'''
|
||||
fields = '''
|
||||
`tabCommunication`.name, `tabCommunication`.communication_type, `tabCommunication`.communication_medium,
|
||||
`tabCommunication`.comment_type, `tabCommunication`.communication_date, `tabCommunication`.content,
|
||||
`tabCommunication`.sender, `tabCommunication`.sender_full_name, `tabCommunication`.cc, `tabCommunication`.bcc,
|
||||
`tabCommunication`.creation, `tabCommunication`.subject, `tabCommunication`.delivery_status,
|
||||
`tabCommunication`._liked_by, `tabCommunication`.reference_doctype, `tabCommunication`.reference_name,
|
||||
`tabCommunication`.read_by_recipient, `tabCommunication`.rating
|
||||
'''
|
||||
|
||||
conditions = '''communication_type in ('Communication', 'Feedback')
|
||||
and (
|
||||
(reference_doctype=%(doctype)s and reference_name=%(name)s)
|
||||
conditions = '''
|
||||
`tabCommunication`.communication_type in ('Communication', 'Feedback')
|
||||
and (
|
||||
(`tabCommunication`.reference_doctype=%(doctype)s and `tabCommunication`.reference_name=%(name)s)
|
||||
or (
|
||||
(timeline_doctype=%(doctype)s and timeline_name=%(name)s)
|
||||
and (communication_type='Communication')
|
||||
(`tabDynamic Link`.link_doctype=%(doctype)s and `tabDynamic Link`.link_name=%(name)s)
|
||||
and (`tabCommunication`.communication_type='Communication')
|
||||
)
|
||||
)'''
|
||||
|
||||
)
|
||||
'''
|
||||
|
||||
if after:
|
||||
# find after a particular date
|
||||
conditions+= ' and creation > {0}'.format(after)
|
||||
conditions += '''
|
||||
and `tabCommunication`.creation > {0}
|
||||
'''.format(after)
|
||||
|
||||
if doctype=='User':
|
||||
conditions+= " and not (reference_doctype='User' and communication_type='Communication')"
|
||||
conditions += '''
|
||||
and not (`tabCommunication`.reference_doctype='User' and `tabCommunication`.communication_type='Communication')
|
||||
'''
|
||||
|
||||
communications = frappe.db.sql("""select {fields}
|
||||
communications = frappe.db.sql('''
|
||||
select distinct {fields}
|
||||
from `tabCommunication`
|
||||
inner join `tabDynamic Link`
|
||||
on `tabCommunication`.name=`tabDynamic Link`.parent
|
||||
where {conditions} {group_by}
|
||||
order by creation desc LIMIT %(limit)s OFFSET %(start)s""".format(
|
||||
fields = fields, conditions=conditions, group_by=group_by or ""),
|
||||
{ "doctype": doctype, "name": name, "start": frappe.utils.cint(start), "limit": limit },
|
||||
as_dict=as_dict)
|
||||
order by `tabCommunication`.creation desc
|
||||
limit %(limit)s offset %(start)s'''.format(fields = fields, conditions=conditions, group_by=group_by or ""),{
|
||||
"doctype": doctype,
|
||||
"name": name,
|
||||
"start": frappe.utils.cint(start),
|
||||
"limit": limit
|
||||
}, as_dict=as_dict)
|
||||
|
||||
return communications
|
||||
|
||||
|
|
@ -218,21 +232,6 @@ def run_onload(doc):
|
|||
doc.set("__onload", frappe._dict())
|
||||
doc.run_method("onload")
|
||||
|
||||
def get_feedback_rating(doctype, docname):
|
||||
""" get and return the latest feedback rating if available """
|
||||
|
||||
rating= frappe.get_all("Communication", filters={
|
||||
"reference_doctype": doctype,
|
||||
"reference_name": docname,
|
||||
"communication_type": "Feedback"
|
||||
}, fields=["rating"], order_by="creation desc", as_list=True)
|
||||
|
||||
if not rating:
|
||||
return 0
|
||||
else:
|
||||
return rating[0][0]
|
||||
|
||||
|
||||
def get_view_logs(doctype, docname):
|
||||
""" get and return the latest view logs if available """
|
||||
logs = []
|
||||
|
|
@ -244,4 +243,4 @@ def get_view_logs(doctype, docname):
|
|||
|
||||
if view_logs:
|
||||
logs = view_logs
|
||||
return logs
|
||||
return logs
|
||||
|
|
@ -27,11 +27,8 @@ def savedocs(doc, action):
|
|||
|
||||
# update recent documents
|
||||
run_onload(doc)
|
||||
frappe.get_user().update_recent(doc.doctype, doc.name)
|
||||
send_updated_docs(doc)
|
||||
except Exception:
|
||||
if not frappe.local.message_log:
|
||||
frappe.msgprint(frappe._('Did not save'))
|
||||
frappe.errprint(frappe.utils.get_traceback())
|
||||
raise
|
||||
|
||||
|
|
|
|||
28
frappe/desk/link_preview.py
Normal file
28
frappe/desk/link_preview.py
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import frappe
|
||||
from frappe.model import no_value_fields
|
||||
import json
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_preview_data(doctype, docname, fields):
|
||||
fields = json.loads(fields)
|
||||
preview_fields = [field['name'] for field in fields if field['type'] not in no_value_fields]
|
||||
preview_fields.append(frappe.get_meta(doctype).get_title_field())
|
||||
if 'name' not in fields:
|
||||
preview_fields.append('name')
|
||||
preview_fields.append(frappe.get_meta(doctype).image_field)
|
||||
|
||||
preview_data = frappe.get_list(doctype, filters={
|
||||
'name': docname
|
||||
}, fields=preview_fields, limit=1)
|
||||
if preview_data:
|
||||
preview_data = preview_data[0]
|
||||
|
||||
preview_data = {k: v for k, v in preview_data.items() if v is not None}
|
||||
for k,v in preview_data.items():
|
||||
if frappe.get_meta(doctype).has_field(k):
|
||||
preview_data[k] = frappe.format(v,frappe.get_meta(doctype).get_field(k).fieldtype)
|
||||
|
||||
if not preview_data:
|
||||
return None
|
||||
return preview_data
|
||||
|
||||
|
|
@ -383,7 +383,7 @@ def get_report_list(module, is_standard="No"):
|
|||
out.append({
|
||||
"type": "report",
|
||||
"doctype": r.ref_doctype,
|
||||
"is_query_report": 1 if r.report_type in ("Query Report", "Script Report") else 0,
|
||||
"is_query_report": 1 if r.report_type in ("Query Report", "Script Report", "Custom Report") else 0,
|
||||
"label": _(r.name),
|
||||
"name": r.name
|
||||
})
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ def get_feed(start, page_length):
|
|||
{match_conditions_comment}
|
||||
) X
|
||||
order by X.creation DESC
|
||||
limit %(start)s, %(page_length)s"""
|
||||
LIMIT %(page_length)s
|
||||
OFFSET %(start)s"""
|
||||
.format(match_conditions_comment = match_conditions_comment,
|
||||
match_conditions_communication = match_conditions_communication), {
|
||||
"user": frappe.session.user,
|
||||
|
|
@ -55,4 +56,4 @@ def get_heatmap_data():
|
|||
where
|
||||
date(creation) > subdate(curdate(), interval 1 year)
|
||||
group by date(creation)
|
||||
order by creation asc"""))
|
||||
order by creation asc"""))
|
||||
|
|
|
|||
|
|
@ -389,7 +389,7 @@ def make_records(records, debug=False):
|
|||
# pass DuplicateEntryError and continue
|
||||
if e.args and e.args[0]==doc.doctype and e.args[1]==doc.name:
|
||||
# make sure DuplicateEntryError is for the exact same doc and not a related doc
|
||||
pass
|
||||
frappe.clear_messages()
|
||||
else:
|
||||
raise
|
||||
|
||||
|
|
|
|||
|
|
@ -132,6 +132,8 @@ def background_enqueue_run(report_name, filters=None, user=None):
|
|||
})
|
||||
track_instance.insert(ignore_permissions=True)
|
||||
frappe.db.commit()
|
||||
track_instance.enqueue_report()
|
||||
|
||||
return {
|
||||
"name": track_instance.name,
|
||||
"redirect_url": get_url_to_form("Prepared Report", track_instance.name)
|
||||
|
|
@ -224,7 +226,7 @@ def add_data_to_custom_columns(columns, result):
|
|||
fieldname = column['fieldname']
|
||||
key = (column['doctype'], fieldname)
|
||||
link_field = column['link_field']
|
||||
row[fieldname] = custom_fields_data.get(key, {}).get(row[link_field])
|
||||
row[fieldname] = custom_fields_data.get(key, {}).get(row.get(link_field))
|
||||
|
||||
return data
|
||||
|
||||
|
|
@ -280,6 +282,10 @@ def export_query():
|
|||
filters = json.loads(data["filters"])
|
||||
if isinstance(data.get("report_name"), string_types):
|
||||
report_name = data["report_name"]
|
||||
frappe.permissions.can_export(
|
||||
frappe.get_cached_value('Report', report_name, 'ref_doctype'),
|
||||
raise_exception=True
|
||||
)
|
||||
if isinstance(data.get("file_format_type"), string_types):
|
||||
file_format_type = data["file_format_type"]
|
||||
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ def save_report():
|
|||
d.report_type = "Report Builder"
|
||||
d.json = data['json']
|
||||
frappe.get_doc(d).save()
|
||||
frappe.msgprint(_("{0} is saved").format(d.name))
|
||||
frappe.msgprint(_("{0} is saved").format(d.name), alert=True)
|
||||
return d.name
|
||||
|
||||
@frappe.whitelist()
|
||||
|
|
|
|||
|
|
@ -54,9 +54,9 @@ frappe.ui.form.on('Auto Email Report', {
|
|||
show_filters: function(frm) {
|
||||
var wrapper = $(frm.get_field('filters_display').wrapper);
|
||||
wrapper.empty();
|
||||
if(frm.doc.report_type !== 'Report Builder'
|
||||
if(frm.doc.report_type === 'Custom Report' || (frm.doc.report_type !== 'Report Builder'
|
||||
&& frappe.query_reports[frm.doc.report]
|
||||
&& frappe.query_reports[frm.doc.report].filters) {
|
||||
&& frappe.query_reports[frm.doc.report].filters)) {
|
||||
|
||||
// make a table to show filters
|
||||
var table = $('<table class="table table-bordered" style="cursor:pointer; margin:0px;"><thead>\
|
||||
|
|
@ -65,7 +65,17 @@ frappe.ui.form.on('Auto Email Report', {
|
|||
$('<p class="text-muted small">' + __("Click table to edit") + '</p>').appendTo(wrapper);
|
||||
|
||||
var filters = JSON.parse(frm.doc.filters || '{}');
|
||||
var report_filters = frappe.query_reports[frm.doc.report].filters;
|
||||
|
||||
let report_filters;
|
||||
|
||||
if (frm.doc.report_type === 'Custom Report'
|
||||
&& frappe.query_reports[frm.doc.reference_report]
|
||||
&& frappe.query_reports[frm.doc.reference_report].filters) {
|
||||
report_filters = frappe.query_reports[frm.doc.reference_report].filters;
|
||||
} else {
|
||||
report_filters = frappe.query_reports[frm.doc.report].filters;
|
||||
}
|
||||
|
||||
if(report_filters && report_filters.length > 0) {
|
||||
frm.set_value('filter_meta', JSON.stringify(report_filters));
|
||||
}
|
||||
|
|
@ -99,6 +109,14 @@ frappe.ui.form.on('Auto Email Report', {
|
|||
dialog.show();
|
||||
dialog.set_values(filters);
|
||||
})
|
||||
|
||||
// populate dynamic date field selection
|
||||
let date_fields = report_filters
|
||||
.filter(df => df.fieldtype === 'Date')
|
||||
.map(df => ({ label: df.label, value: df.fieldname }));
|
||||
frm.set_df_property('from_date_field', 'options', date_fields);
|
||||
frm.set_df_property('to_date_field', 'options', date_fields);
|
||||
frm.toggle_display('dynamic_report_filters_section', date_fields.length > 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue