You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

478 line
13 KiB

  1. // Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
  2. // MIT License. See license.txt
  3. wn.provide('wn.model');
  4. wn.provide("wn.model.map_info");
  5. $.extend(wn.model, {
  6. no_value_type: ['Section Break', 'Column Break', 'HTML', 'Table',
  7. 'Button', 'Image'],
  8. std_fields_list: ['name', 'owner', 'creation', 'modified', 'modified_by',
  9. '_user_tags', 'docstatus', 'parent', 'parenttype', 'parentfield', 'idx'],
  10. std_fields: [
  11. {fieldname:'name', fieldtype:'Link', label:'ID'},
  12. {fieldname:'owner', fieldtype:'Data', label:'Created By'},
  13. {fieldname:'creation', fieldtype:'Date', label:'Created On'},
  14. {fieldname:'modified', fieldtype:'Date', label:'Last Updated On'},
  15. {fieldname:'modified_by', fieldtype:'Data', label:'Last Updated By'},
  16. {fieldname:'_user_tags', fieldtype:'Data', label:'Tags'},
  17. {fieldname:'docstatus', fieldtype:'Int', label:'Document Status'},
  18. ],
  19. std_fields_table: [
  20. {fieldname:'parent', fieldtype:'Data', label:'Parent'},
  21. {fieldname:'idx', fieldtype:'Int', label:'Row No.'},
  22. ],
  23. new_names: {},
  24. events: {},
  25. get_std_field: function(fieldname) {
  26. var docfield = $.map([].concat(wn.model.std_fields).concat(wn.model.std_fields_table),
  27. function(d) {
  28. if(d.fieldname==fieldname) return d;
  29. });
  30. if(!docfield.length) {
  31. msgprint("Unknown Column: " + fieldname);
  32. }
  33. return docfield[0];
  34. },
  35. with_doctype: function(doctype, callback) {
  36. if(locals.DocType[doctype]) {
  37. callback();
  38. } else {
  39. var cached_timestamp = null;
  40. if(localStorage["_doctype:" + doctype]) {
  41. var cached_doclist = JSON.parse(localStorage["_doctype:" + doctype]);
  42. cached_timestamp = cached_doclist[0].modified;
  43. }
  44. return wn.call({
  45. method:'webnotes.widgets.form.load.getdoctype',
  46. type: "GET",
  47. args: {
  48. doctype: doctype,
  49. with_parent: 1,
  50. cached_timestamp: cached_timestamp
  51. },
  52. callback: function(r) {
  53. if(r.exc) {
  54. wn.msgprint(wn._("Unable to load") + ": " + wn._(doctype));
  55. throw "No doctype";
  56. return;
  57. }
  58. if(r.message=="use_cache") {
  59. wn.model.sync(cached_doclist);
  60. } else {
  61. localStorage["_doctype:" + doctype] = JSON.stringify(r.docs);
  62. }
  63. wn.model.init_doctype(doctype);
  64. callback(r);
  65. }
  66. });
  67. }
  68. },
  69. init_doctype: function(doctype) {
  70. var meta = locals.DocType[doctype];
  71. if(meta.__list_js) {
  72. eval(meta.__list_js);
  73. }
  74. if(meta.__calendar_js) {
  75. eval(meta.__calendar_js);
  76. }
  77. if(meta.__map_js) {
  78. eval(meta.__map_js);
  79. }
  80. },
  81. with_doc: function(doctype, name, callback) {
  82. if(!name) name = doctype; // single type
  83. if(locals[doctype] && locals[doctype][name] && wn.model.get_docinfo(doctype, name)) {
  84. callback(name);
  85. } else {
  86. return wn.call({
  87. method: 'webnotes.widgets.form.load.getdoc',
  88. type: "GET",
  89. args: {
  90. doctype: doctype,
  91. name: name
  92. },
  93. callback: function(r) { callback(name, r); }
  94. });
  95. }
  96. },
  97. get_docinfo: function(doctype, name) {
  98. return wn.model.docinfo[doctype] && wn.model.docinfo[doctype][name] || null;
  99. },
  100. get_server_module_name: function(doctype) {
  101. var dt = wn.model.scrub(doctype)
  102. return wn.model.scrub(locals.DocType[doctype].module)
  103. + '.doctype.' + dt + '.' + dt;
  104. },
  105. scrub: function(txt) {
  106. return txt.replace(/ /g, "_").toLowerCase();
  107. },
  108. can_create: function(doctype) {
  109. return wn.boot.profile.can_create.indexOf(doctype)!=-1;
  110. },
  111. can_read: function(doctype) {
  112. return wn.boot.profile.can_read.indexOf(doctype)!=-1;
  113. },
  114. can_write: function(doctype) {
  115. return wn.boot.profile.can_write.indexOf(doctype)!=-1;
  116. },
  117. can_get_report: function(doctype) {
  118. return wn.boot.profile.can_get_report.indexOf(doctype)!=-1;
  119. },
  120. can_delete: function(doctype) {
  121. if(!doctype) return false;
  122. return wn.boot.profile.can_cancel.indexOf(doctype)!=-1;
  123. },
  124. is_submittable: function(doctype) {
  125. if(!doctype) return false;
  126. return locals.DocType[doctype] && locals.DocType[doctype].is_submittable;
  127. },
  128. has_value: function(dt, dn, fn) {
  129. // return true if property has value
  130. var val = locals[dt] && locals[dt][dn] && locals[dt][dn][fn];
  131. var df = wn.meta.get_docfield(dt, fn, dn);
  132. if(df.fieldtype=='Table') {
  133. var ret = false;
  134. $.each(locals[df.options] || {}, function(k,d) {
  135. if(d.parent==dn && d.parenttype==dt && d.parentfield==df.fieldname) {
  136. ret = true;
  137. }
  138. });
  139. } else {
  140. var ret = !is_null(val);
  141. }
  142. return ret ? true : false;
  143. },
  144. get: function(doctype, filters) {
  145. var src = locals[doctype] || locals[":" + doctype] || [];
  146. if($.isEmptyObject(src))
  147. return [];
  148. return wn.utils.filter_dict(src, filters);
  149. },
  150. get_value: function(doctype, filters, fieldname) {
  151. if(typeof filters==="string") {
  152. return locals[doctype] && locals[doctype][filters]
  153. && locals[doctype][filters][fieldname];
  154. } else {
  155. var l = wn.model.get(doctype, filters);
  156. return (l.length && l[0]) ? l[0][fieldname] : null;
  157. }
  158. },
  159. set_value: function(doctype, name, fieldname, value, fieldtype) {
  160. /* help: Set a value locally (if changed) and execute triggers */
  161. if(!name) name = doctype;
  162. var doc = locals[doctype] && locals[doctype][name] || null;
  163. if(doc && doc[fieldname] !== value) {
  164. doc[fieldname] = value;
  165. wn.model.trigger(fieldname, value, doc);
  166. return true;
  167. } else {
  168. // execute link triggers (want to reselect to execute triggers)
  169. if(fieldtype=="Link")
  170. wn.model.trigger(fieldname, value, doc);
  171. }
  172. },
  173. on: function(doctype, fieldname, fn) {
  174. /* help: Attach a trigger on change of a particular field.
  175. To trigger on any change in a particular doctype, use fieldname as "*"
  176. */
  177. /* example: wn.model.on("Customer", "age", function(fieldname, value, doc) {
  178. if(doc.age < 16) {
  179. msgprint("Warning, Customer must atleast be 16 years old.");
  180. raise "CustomerAgeError";
  181. }
  182. }) */
  183. wn.provide("wn.model.events." + doctype);
  184. if(!wn.model.events[doctype][fieldname]) {
  185. wn.model.events[doctype][fieldname] = [];
  186. }
  187. wn.model.events[doctype][fieldname].push(fn);
  188. },
  189. trigger: function(fieldname, value, doc) {
  190. var run = function(events, event_doc) {
  191. $.each(events || [], function(i, fn) {
  192. fn && fn(fieldname, value, event_doc || doc);
  193. });
  194. };
  195. if(wn.model.events[doc.doctype]) {
  196. // field-level
  197. run(wn.model.events[doc.doctype][fieldname]);
  198. // doctype-level
  199. run(wn.model.events[doc.doctype]['*']);
  200. };
  201. },
  202. get_doc: function(doctype, name) {
  203. return locals[doctype] ? locals[doctype][name] : null;
  204. },
  205. get_doclist: function(doctype, name, filters) {
  206. var doclist = [];
  207. if(!locals[doctype])
  208. return doclist;
  209. doclist[0] = locals[doctype][name];
  210. $.each(wn.model.get("DocField", {parent:doctype, fieldtype:"Table"}),
  211. function(i, table_field) {
  212. var child_doclist = wn.model.get(table_field.options, {
  213. parent:name, parenttype: doctype,
  214. parentfield: table_field.fieldname});
  215. if($.isArray(child_doclist)) {
  216. child_doclist.sort(function(a, b) { return a.idx - b.idx; });
  217. doclist = doclist.concat(child_doclist);
  218. }
  219. }
  220. );
  221. if(filters) {
  222. doclist = wn.utils.filter_dict(doclist, filters);
  223. }
  224. return doclist;
  225. },
  226. get_children: function(doctype, parent, parentfield, parenttype) {
  227. if(parenttype) {
  228. var l = wn.model.get(doctype, {parent:parent,
  229. parentfield:parentfield, parenttype:parenttype});
  230. } else {
  231. var l = wn.model.get(doctype, {parent:parent,
  232. parentfield:parentfield});
  233. }
  234. if(l.length) {
  235. l.sort(function(a,b) { return flt(a.idx) - flt(b.idx) });
  236. // renumber
  237. $.each(l, function(i, v) { v.idx = i+1; }); // for chrome bugs ???
  238. }
  239. return l;
  240. },
  241. clear_doclist: function(doctype, name) {
  242. $.each(wn.model.get_doclist(doctype, name), function(i, d) {
  243. if(d) wn.model.clear_doc(d.doctype, d.name);
  244. });
  245. },
  246. clear_table: function(doctype, parenttype, parent, parentfield) {
  247. $.each(locals[doctype] || {}, function(i, d) {
  248. if(d.parent===parent && d.parenttype===parenttype && d.parentfield===parentfield) {
  249. delete locals[doctype][d.name];
  250. }
  251. })
  252. },
  253. remove_from_locals: function(doctype, name) {
  254. this.clear_doclist(doctype, name);
  255. if(wn.views.formview[doctype]) {
  256. delete wn.views.formview[doctype].frm.opendocs[name];
  257. }
  258. },
  259. clear_doc: function(doctype, name) {
  260. var doc = locals[doctype][name];
  261. if(doc && doc.parenttype) {
  262. var parent = doc.parent,
  263. parenttype = doc.parenttype,
  264. parentfield = doc.parentfield;
  265. }
  266. delete locals[doctype][name];
  267. if(parent)
  268. wn.model.get_children(doctype, parent, parentfield, parenttype);
  269. },
  270. get_no_copy_list: function(doctype) {
  271. var no_copy_list = ['name','amended_from','amendment_date','cancel_reason'];
  272. $.each(wn.model.get("DocField", {parent:doctype}), function(i, df) {
  273. if(cint(df.no_copy)) no_copy_list.push(df.fieldname);
  274. })
  275. return no_copy_list;
  276. },
  277. copy_doc: function(dt, dn, from_amend) {
  278. var no_copy_list = wn.model.get_no_copy_list(dt);
  279. var newdoc = wn.model.get_new_doc(dt);
  280. for(var key in locals[dt][dn]) {
  281. // dont copy name and blank fields
  282. if(key.substr(0,2)!='__'
  283. && !in_list(no_copy_list, key)) {
  284. newdoc[key] = locals[dt][dn][key];
  285. }
  286. }
  287. return newdoc;
  288. },
  289. // args: source (doclist), target (doctype), table_map, field_map, callback
  290. map: function(args) {
  291. wn.model.with_doctype(args.target, function() {
  292. var map_info = wn.model.map_info[args.target]
  293. if(map_info)
  294. map_info = map_info[args.source[0].doctype];
  295. if(!map_info) {
  296. map_info = {
  297. table_map: args.table_map || {},
  298. field_map: args.field_map || {}
  299. }
  300. }
  301. // main
  302. var target = wn.model.map_doc(args.source[0], args.target, map_info.field_map[args.target]);
  303. // children
  304. $.each(map_info.table_map, function(child_target, child_source) {
  305. $.each($.map(args.source, function(d)
  306. { if(d.doctype==child_source) return d; else return null; }), function(i, d) {
  307. var child = wn.model.map_doc(d, child_target, map_info.field_map[child_target]);
  308. $.extend(child, {
  309. parent: target.name,
  310. parenttype: target.doctype,
  311. parentfield: wn.meta.get_parentfield(target.doctype, child.doctype),
  312. idx: i+1
  313. });
  314. });
  315. });
  316. if(args.callback) {
  317. args.callback(target);
  318. } else {
  319. wn.set_route("Form", target.doctype, target.name);
  320. }
  321. });
  322. },
  323. // map a single doc to a new doc of given DocType and field_map
  324. map_doc: function(source, doctype, field_map) {
  325. var new_doc = wn.model.get_new_doc(doctype);
  326. var no_copy_list = wn.model.get_no_copy_list(doctype);
  327. if(!field_map) field_map = {};
  328. delete no_copy_list[no_copy_list.indexOf("name")];
  329. for(fieldname in wn.meta.docfield_map[doctype]) {
  330. var df = wn.meta.docfield_map[doctype][fieldname];
  331. if(!df.no_copy) {
  332. var source_key = field_map[df.fieldname] || df.fieldname;
  333. if(source_key.substr(0,1)=="=") {
  334. var value = source_key.substr(1);
  335. } else {
  336. var value = source[source_key];
  337. }
  338. if(value!==undefined) {
  339. new_doc[df.fieldname] = value;
  340. }
  341. }
  342. }
  343. return new_doc;
  344. },
  345. delete_doc: function(doctype, docname, callback) {
  346. wn.confirm("Permanently delete "+ docname + "?", function() {
  347. return wn.call({
  348. method: 'webnotes.model.delete_doc',
  349. args: {
  350. dt:doctype,
  351. dn:docname
  352. },
  353. callback: function(r, rt) {
  354. if(!r.exc) {
  355. wn.model.clear_doclist(doctype, docname);
  356. if(wn.ui.toolbar.recent)
  357. wn.ui.toolbar.recent.remove(doctype, docname);
  358. if(callback) callback(r,rt);
  359. }
  360. }
  361. })
  362. })
  363. },
  364. rename_doc: function(doctype, docname, callback) {
  365. var d = new wn.ui.Dialog({
  366. title: "Rename " + docname,
  367. fields: [
  368. {label:"New Name", fieldtype:"Data", reqd:1},
  369. {label:"Merge with existing", fieldtype:"Check", fieldname:"merge"},
  370. {label:"Rename", fieldtype: "Button"}
  371. ]
  372. });
  373. d.get_input("rename").on("click", function() {
  374. var args = d.get_values();
  375. if(!args) return;
  376. d.get_input("rename").set_working();
  377. return wn.call({
  378. method:"webnotes.model.rename_doc.rename_doc",
  379. args: {
  380. doctype: doctype,
  381. old: docname,
  382. "new": args.new_name,
  383. "merge": args.merge
  384. },
  385. callback: function(r,rt) {
  386. d.get_input("rename").done_working();
  387. if(!r.exc) {
  388. $(document).trigger('rename', [doctype, docname,
  389. r.message || args.new_name]);
  390. if(locals[doctype] && locals[doctype][docname])
  391. delete locals[doctype][docname];
  392. d.hide();
  393. if(callback)
  394. callback(r.message);
  395. }
  396. }
  397. });
  398. });
  399. d.show();
  400. },
  401. round_floats_in: function(doc, fieldnames) {
  402. if(!fieldnames) {
  403. fieldnames = wn.meta.get_fieldnames(doc.doctype, doc.name,
  404. {"fieldtype": ["in", ["Currency", "Float"]]});
  405. }
  406. $.each(fieldnames, function(i, fieldname) {
  407. doc[fieldname] = flt(doc[fieldname], precision(fieldname, doc));
  408. });
  409. },
  410. validate_missing: function(doc, fieldname) {
  411. if(!doc[fieldname]) {
  412. wn.throw(wn._("Please specify") + ": " +
  413. wn._(wn.meta.get_label(doc.doctype, fieldname, doc.parent || doc.name)));
  414. }
  415. }
  416. });
  417. // legacy
  418. getchildren = wn.model.get_children
  419. make_doclist = wn.model.get_doclist