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 年之前
11 年之前
11 年之前
13 年之前
13 年之前
13 年之前
13 年之前
12 年之前
11 年之前
11 年之前
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