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.

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