diff --git a/frappe/core/doctype/docfield/docfield.txt b/frappe/core/doctype/docfield/docfield.txt index 12c02d8fff..2dd3a6e819 100644 --- a/frappe/core/doctype/docfield/docfield.txt +++ b/frappe/core/doctype/docfield/docfield.txt @@ -2,7 +2,7 @@ { "creation": "2013-02-22 01:27:33", "docstatus": 0, - "modified": "2014-01-02 10:50:02", + "modified": "2014-03-05 14:58:43", "modified_by": "Administrator", "owner": "Administrator" }, @@ -178,6 +178,13 @@ "print_width": "50px", "width": "50px" }, + { + "doctype": "DocField", + "fieldname": "set_only_once", + "fieldtype": "Check", + "label": "Set Only Once", + "description": "Do not allow user to change after set the first time" + }, { "doctype": "DocField", "fieldname": "column_break_13", diff --git a/frappe/data/Framework.sql b/frappe/data/Framework.sql index b4e2a53b9c..f6b04d5d26 100644 --- a/frappe/data/Framework.sql +++ b/frappe/data/Framework.sql @@ -26,6 +26,7 @@ CREATE TABLE `tabDocField` ( `options` text, `search_index` int(1) DEFAULT NULL, `hidden` int(1) DEFAULT NULL, + `set_only_once` int(1) DEFAULT NULL, `print_hide` int(1) DEFAULT NULL, `report_hide` int(1) DEFAULT NULL, `reqd` int(1) DEFAULT NULL, diff --git a/frappe/exceptions.py b/frappe/exceptions.py index ecd9957531..0bbc5a5825 100644 --- a/frappe/exceptions.py +++ b/frappe/exceptions.py @@ -33,3 +33,4 @@ class InvalidStatusError(ValidationError): pass class MandatoryError(ValidationError): pass class InvalidSignatureError(ValidationError): pass class RateLimitExceededError(ValidationError): pass +class CannotChangeConstantError(ValidationError): pass diff --git a/frappe/model/doc.py b/frappe/model/doc.py index 5df4d1d25a..91eeed172b 100755 --- a/frappe/model/doc.py +++ b/frappe/model/doc.py @@ -9,6 +9,7 @@ Contains the Document class representing an object / record _toc = ["frappe.model.doc.Document"] import frappe +from frappe import _ import frappe.model.meta from frappe.utils import * @@ -229,7 +230,7 @@ class Document: self.set_idx() # if required, make new - if not self._meta.issingle: + if not self._meta[0].issingle: if self.is_new(): r = self._insert(make_autoname=make_autoname, keep_timestamps = keep_timestamps) if r: @@ -241,7 +242,7 @@ class Document: # save the values - self._update_values(self._meta.issingle, + self._update_values(self._meta[0].issingle, check_links and self.make_link_list() or {}, ignore_fields=ignore_fields, keep_timestamps=keep_timestamps) self._clear_temp_fields() @@ -264,7 +265,7 @@ class Document: self._new_name_set = True self.get_meta() - autoname = self._meta.autoname + autoname = self._meta[0].autoname self.localname = self.name @@ -305,7 +306,7 @@ class Document: self.name = self.fields['__newname'] # default name for table - elif self._meta.istable: + elif self._meta[0].istable: self.name = make_autoname('#########', self.doctype) # unable to determine a name, use global series @@ -342,7 +343,7 @@ class Document: self.set_new_name() # validate name - self.name = validate_name(self.doctype, self.name, self._meta.name_case) + self.name = validate_name(self.doctype, self.name, self._meta[0].name_case) # insert! if not keep_timestamps: @@ -422,6 +423,7 @@ class Document: return tmp and tmp[0][0] or ''# match case def _update_values(self, issingle, link_list, ignore_fields=0, keep_timestamps=False): + self.validate_constants() if issingle: self._update_single(link_list) else: @@ -463,7 +465,7 @@ class Document: valid_fields_map = frappe.local.valid_fields_map if not valid_fields_map.get(self.doctype): - if cint( self._meta.issingle): + if cint( self._meta[0].issingle): doctypelist = frappe.model.doctype.get(self.doctype) valid_fields_map[self.doctype] = doctypelist.get_fieldnames({ "fieldtype": ["not in", frappe.model.no_value_fields]}) @@ -473,10 +475,20 @@ class Document: return valid_fields_map.get(self.doctype) + def validate_constants(self): + self.get_meta() + constants = [d.fieldname for d in self._meta.get({"set_only_once": 1})] + if constants: + values = frappe.db.get_value(self.doctype, self.name, constants, as_dict=True) + + for fieldname in constants: + if self.fields.get(fieldname) != values.get(fieldname): + frappe.throw("{0}: {1}".format(_("Value cannot be changed for"), + _(self._meta.get_field(fieldname).label)), frappe.CannotChangeConstantError) + def get_meta(self): if not self._meta: - self._meta = frappe.db.get_value("DocType", self.doctype, ["autoname", "issingle", - "istable", "name_case"], as_dict=True) or frappe._dict() + self._meta = frappe.get_doctype(self.doctype) return self._meta diff --git a/frappe/patches.txt b/frappe/patches.txt index 1455d35e14..655217e402 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -1,7 +1,7 @@ execute:import inlinestyler # new requirement execute:frappe.reload_doc('core', 'doctype', 'doctype', force=True) #2014-01-24 -execute:frappe.reload_doc('core', 'doctype', 'docfield', force=True) #2013-13-26 +execute:frappe.reload_doc('core', 'doctype', 'docfield', force=True) #2014-03-01 execute:frappe.reload_doc('core', 'doctype', 'docperm') #2013-13-26 execute:frappe.reload_doc('core', 'doctype', 'page') #2013-13-26 execute:frappe.reload_doc('core', 'doctype', 'report') #2013-13-26 @@ -21,4 +21,4 @@ execute:import frappe.installer;frappe.installer.make_site_dirs() #2014-02-19 frappe.patches.4_0.private_backups frappe.patches.4_0.set_module_in_report frappe.patches.4_0.remove_old_parent -frappe.patches.4_0.rename_profile_to_user \ No newline at end of file +frappe.patches.4_0.rename_profile_to_user diff --git a/frappe/website/doctype/blog_post/test_blog_post.py b/frappe/website/doctype/blog_post/test_blog_post.py index 58f878e7c6..ac42ccb289 100644 --- a/frappe/website/doctype/blog_post/test_blog_post.py +++ b/frappe/website/doctype/blog_post/test_blog_post.py @@ -161,3 +161,12 @@ class TestBlogPost(unittest.TestCase): bean = frappe.bean("Blog Post", "_test-blog-post-1") self.assertTrue(bean.has_read_perm()) + + def test_set_only_once(self): + blog_post = frappe.get_doctype("Blog Post") + blog_post.get_field("title").set_only_once = 1 + bean = frappe.bean("Blog Post", "_test-blog-post-1") + bean.doc.title = "New" + self.assertRaises(frappe.CannotChangeConstantError, bean.save) + blog_post.get_field("title").set_only_once = 0 +