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.

611 lines
17 KiB

  1. package controller;
  2. import db.UserContract;
  3. import sugoi.form.elements.DateDropdowns;
  4. import sugoi.form.elements.Input;
  5. import sugoi.form.elements.Selectbox;
  6. import sugoi.form.Form;
  7. import db.Contract;
  8. import Common;
  9. import plugin.Tutorial;
  10. using Std;
  11. import service.OrderService;
  12. class Contract extends Controller {
  13. public function new() {
  14. super();
  15. }
  16. @tpl("contract/view.mtt")
  17. public function doView(c:db.Contract) {
  18. view.category = 'amap';
  19. view.c = c;
  20. }
  21. /**
  22. * "my account" page
  23. */
  24. @tpl("contract/default.mtt")
  25. function doDefault() {
  26. // Create the list of links to change the language
  27. var langs = App.config.get("langs", "fr").split(";");
  28. var langNames = App.config.get("langnames", "Français").split(";");
  29. var i = 0;
  30. var langLinks = "";
  31. for (lang in langs) {
  32. langLinks += "<li><a href=\"?lang=" + langs[i] + "\">" + langNames[i] + "</a></li>";
  33. i++;
  34. }
  35. view.langLinks = langLinks;
  36. view.langText = langNames[langs.indexOf(app.session.lang)];
  37. // change account lang
  38. if (app.params.exists("lang") && app.user != null) {
  39. app.user.lock();
  40. app.user.lang = app.params.get("lang");
  41. app.user.update();
  42. }
  43. var ua = db.UserAmap.get(app.user, app.user.amap);
  44. if (ua == null)
  45. throw Error("/", t._("You are not a member of this group"));
  46. var constOrders = null;
  47. var varOrders = new Map<String, Array<db.UserContract>>();
  48. var a = App.current.user.amap;
  49. var oneMonthAgo = DateTools.delta(Date.now(), -1000.0 * 60 * 60 * 24 * 30);
  50. // constant orders
  51. var contracts = db.Contract.manager.search($type == db.Contract.TYPE_CONSTORDERS && $amap == a && $endDate > oneMonthAgo, false);
  52. constOrders = [];
  53. for (c in contracts) {
  54. var orders = app.user.getOrdersFromContracts([c]);
  55. if (orders.length == 0)
  56. continue;
  57. constOrders.push({contract: c, orders: service.OrderService.prepare(orders)});
  58. }
  59. // variable orders, grouped by date
  60. var contracts = db.Contract.manager.search($type == db.Contract.TYPE_VARORDER && $amap == a && $endDate > oneMonthAgo, false);
  61. for (c in contracts) {
  62. var ds = c.getDistribs(false);
  63. for (d in ds) {
  64. // store orders in a stringmap like "2015-01-01" => [order1,order2,...]
  65. var k = d.date.toString().substr(0, 10);
  66. var orders = app.user.getOrdersFromDistrib(d);
  67. if (orders.length > 0) {
  68. if (!varOrders.exists(k)) {
  69. varOrders.set(k, Lambda.array(orders));
  70. } else {
  71. var z = varOrders.get(k).concat(Lambda.array(orders));
  72. varOrders.set(k, z);
  73. }
  74. }
  75. }
  76. }
  77. // final structure
  78. var varOrders2 = new Array<{date:Date, orders:Array<UserOrder>}>();
  79. for (k in varOrders.keys()) {
  80. var d = new Date(k.split("-")[0].parseInt(), k.split("-")[1].parseInt() - 1, k.split("-")[2].parseInt(), 0, 0, 0);
  81. var orders = service.OrderService.prepare(Lambda.list(varOrders[k]));
  82. varOrders2.push({date: d, orders: orders});
  83. }
  84. // sort by date desc
  85. varOrders2.sort(function(b, a) {
  86. return Math.round(a.date.getTime() / 1000) - Math.round(b.date.getTime() / 1000);
  87. });
  88. view.varOrders = varOrders2;
  89. view.constOrders = constOrders;
  90. // tutorials
  91. if (app.user.isAmapManager()) {
  92. // actions
  93. if (app.params.exists('startTuto')) {
  94. // start a tuto
  95. app.user.lock();
  96. var t = app.params.get('startTuto');
  97. app.user.tutoState = {name: t, step: 0};
  98. app.user.update();
  99. }
  100. // tuto state
  101. var tutos = new Array<{name:String, completion:Float, key:String}>();
  102. for (k in Tutorial.all().keys()) {
  103. var t = Tutorial.all().get(k);
  104. var completion = null;
  105. if (app.user.tutoState != null && app.user.tutoState.name == k)
  106. completion = app.user.tutoState.step / t.steps.length;
  107. tutos.push({name: t.name, completion: completion, key: k});
  108. }
  109. view.tutos = tutos;
  110. }
  111. // should be able to stop tuto in any case
  112. if (app.params.exists('stopTuto')) {
  113. // stopped tuto from a tuto window
  114. app.user.lock();
  115. app.user.tutoState = null;
  116. app.user.update();
  117. view.stopTuto = true;
  118. }
  119. checkToken();
  120. view.userAmap = ua;
  121. }
  122. /**
  123. * Edit a contract
  124. */
  125. @tpl("form.mtt")
  126. function doEdit(c:db.Contract) {
  127. view.category = 'contractadmin';
  128. if (!app.user.isContractManager(c))
  129. throw Error('/', t._("Forbidden action"));
  130. view.title = t._("Edit contract ::contractName::", {contractName: c.name});
  131. var group = c.amap;
  132. var currentContact = c.contact;
  133. var form = Form.fromSpod(c);
  134. form.removeElement(form.getElement("amapId"));
  135. form.removeElement(form.getElement("type"));
  136. form.getElement("userId").required = true;
  137. app.event(EditContract(c, form));
  138. if (form.checkToken()) {
  139. form.toSpod(c);
  140. c.amap = group;
  141. // checks & warnings
  142. if (c.hasPercentageOnOrders() && c.percentageValue == null)
  143. throw Error("/contract/edit/" + c.id, t._("If you would like to add fees to the order, define a rate (%) and a label."));
  144. if (c.hasStockManagement()) {
  145. for (p in c.getProducts()) {
  146. if (p.stock == null) {
  147. app.session.addMessage(t._("Warning about management of stock. Please fill the field \"stock\" for all your products"), true);
  148. break;
  149. }
  150. }
  151. }
  152. // no stock mgmt for constant orders
  153. if (c.hasStockManagement() && c.type == db.Contract.TYPE_CONSTORDERS) {
  154. c.flags.unset(ContractFlags.StockManagement);
  155. app.session.addMessage(t._("Managing stock is not available for CSA contracts"), true);
  156. }
  157. c.update();
  158. // update rights
  159. if (c.contact != null && (currentContact == null || c.contact.id != currentContact.id)) {
  160. var ua = db.UserAmap.get(c.contact, app.user.amap, true);
  161. ua.giveRight(ContractAdmin(c.id));
  162. ua.giveRight(Messages);
  163. ua.giveRight(Membership);
  164. ua.update();
  165. // remove rights to old contact
  166. if (currentContact != null) {
  167. var x = db.UserAmap.get(currentContact, c.amap, true);
  168. if (x != null) {
  169. x.removeRight(ContractAdmin(c.id));
  170. x.update();
  171. }
  172. }
  173. }
  174. throw Ok("/contractAdmin/view/" + c.id, t._("Contract updated"));
  175. }
  176. view.form = form;
  177. }
  178. @tpl("contract/insertChoose.mtt")
  179. function doInsertChoose() {
  180. // checkToken();
  181. }
  182. /**
  183. * Créé un nouveau contrat
  184. */
  185. @tpl("form.mtt")
  186. function doInsert(?type:Int) {
  187. if (!app.user.canManageAllContracts())
  188. throw Error('/', t._("Forbidden action"));
  189. if (type == null)
  190. throw Redirect('/contract/insertChoose');
  191. view.title = if (type == db.Contract.TYPE_CONSTORDERS) t._("Create a contract with fixed orders") else t._("Create a contract with variable orders");
  192. var c = new db.Contract();
  193. var form = Form.fromSpod(c);
  194. form.removeElement(form.getElement("amapId"));
  195. form.removeElement(form.getElement("type"));
  196. form.getElement("userId").required = true;
  197. if (form.checkToken()) {
  198. form.toSpod(c);
  199. c.amap = app.user.amap;
  200. // trace(app.user.amap);
  201. // trace(c.amap);
  202. c.type = type;
  203. c.insert();
  204. // right
  205. if (c.contact != null) {
  206. var ua = db.UserAmap.get(c.contact, app.user.amap, true);
  207. ua.giveRight(ContractAdmin(c.id));
  208. ua.giveRight(Messages);
  209. ua.giveRight(Membership);
  210. ua.update();
  211. }
  212. throw Ok("/contractAdmin/view/" + c.id, t._("New contract created"));
  213. }
  214. view.form = form;
  215. }
  216. /**
  217. * Delete a contract (... and its products, orders & distributions)
  218. */
  219. function doDelete(c:db.Contract) {
  220. if (!app.user.canManageAllContracts())
  221. throw Error("/contractAdmin", t._("You don't have the authorization to remove a contract"));
  222. if (checkToken()) {
  223. c.lock();
  224. // check if there is orders in this contract
  225. var products = c.getProducts();
  226. var orders = db.UserContract.manager.search($productId in Lambda.map(products, function(p) return p.id));
  227. var qt = 0.0;
  228. for (o in orders)
  229. qt += o.quantity; // there could be "zero c qt" orders
  230. if (qt > 0) {
  231. throw Error("/contractAdmin", t._("You cannot delete this contract because some orders are linked to it."));
  232. }
  233. // remove admin rights and delete contract
  234. if (c.contact != null) {
  235. var ua = db.UserAmap.get(c.contact, c.amap, true);
  236. if (ua != null) {
  237. ua.removeRight(ContractAdmin(c.id));
  238. ua.update();
  239. }
  240. }
  241. app.event(DeleteContract(c));
  242. c.delete();
  243. throw Ok("/contractAdmin", t._("Contract deleted"));
  244. }
  245. throw Error("/contractAdmin", t._("Token error"));
  246. }
  247. /**
  248. * Make an order by contract ( standard mode )
  249. * The form is prepopulated if orders have already been made.
  250. *
  251. * It should work for constant orders ( will display one column )
  252. * or varying orders ( with as many columns as distributions dates )
  253. *
  254. */
  255. @tpl("contract/order.mtt")
  256. function doOrder(c:db.Contract) {
  257. // checks
  258. if (app.user.amap.hasPayments())
  259. throw Redirect("/contract/orderAndPay/" + c.id);
  260. if (app.user.amap.hasShopMode())
  261. throw Redirect("/shop");
  262. if (!c.isUserOrderAvailable())
  263. throw Error("/", t._("This contract is not opened for orders"));
  264. var distributions = [];
  265. // If its a varying contract, we display a column by distribution
  266. if (c.type == db.Contract.TYPE_VARORDER) {
  267. distributions = db.Distribution.getOpenToOrdersDeliveries(c);
  268. } else {
  269. distributions = [null];
  270. }
  271. // list of distribs with a list of product and optionnaly an order
  272. var userOrders = new Array<{distrib:db.Distribution, datas:Array<{order:db.UserContract, product:db.Product}>}>();
  273. var products = c.getProducts();
  274. if (c.type == db.Contract.TYPE_VARORDER) {
  275. for (d in distributions) {
  276. var datas = [];
  277. for (p in products) {
  278. var ua = {order: null, product: p};
  279. var order = db.UserContract.manager.select($user == app.user && $productId == p.id && $distributionId == d.id, true);
  280. if (order != null)
  281. ua.order = order;
  282. datas.push(ua);
  283. }
  284. userOrders.push({distrib: d, datas: datas});
  285. }
  286. } else {
  287. var datas = [];
  288. for (p in products) {
  289. var ua = {order: null, product: p};
  290. var order = db.UserContract.manager.select($user == app.user && $productId == p.id, true);
  291. if (order != null)
  292. ua.order = order;
  293. datas.push(ua);
  294. }
  295. userOrders.push({distrib: null, datas: datas});
  296. }
  297. // form check
  298. if (checkToken()) {
  299. // get dsitrib if needed
  300. // var distrib : db.Distribution = null;
  301. // if (c.type == db.Contract.TYPE_VARORDER) {
  302. // distrib = db.Distribution.manager.get(Std.parseInt(app.params.get("distribution")), false);
  303. // }
  304. for (k in app.params.keys()) {
  305. if (k.substr(0, 1) != "d")
  306. continue;
  307. var qt = app.params.get(k);
  308. if (qt == "")
  309. continue;
  310. var pid = null;
  311. var did = null;
  312. try {
  313. pid = Std.parseInt(k.split("-")[1].substr(1));
  314. did = Std.parseInt(k.split("-")[0].substr(1));
  315. } catch (e:Dynamic) {
  316. trace("unable to parse key " + k);
  317. }
  318. // find related element in userOrders
  319. var uo = null;
  320. for (x in userOrders) {
  321. if (x.distrib != null && x.distrib.id != did) {
  322. continue;
  323. } else {
  324. for (a in x.datas) {
  325. if (a.product.id == pid) {
  326. uo = a;
  327. break;
  328. }
  329. }
  330. }
  331. }
  332. if (uo == null)
  333. throw t._("Could not find the product ::produ:: and delivery ::deliv::", {produ: pid, deliv: did});
  334. var q = 0.0;
  335. if (uo.product.hasFloatQt) {
  336. var param = StringTools.replace(qt, ",", ".");
  337. q = Std.parseFloat(param);
  338. } else {
  339. q = Std.parseInt(qt);
  340. }
  341. if (uo.order != null) {
  342. OrderService.edit(uo.order, q);
  343. } else {
  344. OrderService.make(app.user, q, uo.product, did);
  345. }
  346. }
  347. throw Ok("/contract/order/" + c.id, t._("Your order has been updated"));
  348. }
  349. view.c = view.contract = c;
  350. view.userOrders = userOrders;
  351. }
  352. /**
  353. * Make an order by contract ( standard mode ) + payment process
  354. */
  355. @tpl("contract/orderAndPay.mtt")
  356. function doOrderAndPay(c:db.Contract) {
  357. // checks
  358. if (!app.user.amap.hasPayments())
  359. throw Redirect("/contract/order/" + c.id);
  360. if (app.user.amap.hasShopMode())
  361. throw Redirect("/");
  362. if (!c.isUserOrderAvailable())
  363. throw Error("/", t._("This contract is not opened for orders"));
  364. var distributions = [];
  365. /* If its a varying contract, we display a column by distribution*/
  366. if (c.type == db.Contract.TYPE_VARORDER) {
  367. distributions = db.Distribution.getOpenToOrdersDeliveries(c);
  368. }
  369. // list of distribs with a list of product and optionnaly an order
  370. var userOrders = new Array<{distrib:db.Distribution, datas:Array<{order:db.UserContract, product:db.Product}>}>();
  371. var products = c.getProducts();
  372. for (d in distributions) {
  373. var datas = [];
  374. for (p in products) {
  375. var ua = {order: null, product: p};
  376. var order:db.UserContract = null;
  377. if (c.type == db.Contract.TYPE_VARORDER) {
  378. order = db.UserContract.manager.select($user == app.user && $productId == p.id && $distributionId == d.id, true);
  379. } else {
  380. order = db.UserContract.manager.select($user == app.user && $productId == p.id, true);
  381. }
  382. if (order != null)
  383. ua.order = order;
  384. datas.push(ua);
  385. }
  386. userOrders.push({distrib: d, datas: datas});
  387. }
  388. // form check
  389. if (checkToken()) {
  390. // get distrib if needed
  391. var distrib = null;
  392. if (c.type == db.Contract.TYPE_VARORDER) {
  393. distrib = db.Distribution.manager.get(Std.parseInt(app.params.get("distribution")), false);
  394. }
  395. var orders:OrderInSession = {products: [], userId: app.user.id, total: 0};
  396. for (k in app.params.keys()) {
  397. if (k.substr(0, 1) != "d")
  398. continue;
  399. var qt = app.params.get(k);
  400. if (qt == "")
  401. continue;
  402. var pid = null;
  403. var did = null;
  404. try {
  405. pid = Std.parseInt(k.split("-")[1].substr(1));
  406. did = Std.parseInt(k.split("-")[0].substr(1));
  407. } catch (e:Dynamic) {
  408. trace("unable to parse key " + k);
  409. }
  410. // find related element in userOrders
  411. var uo = null;
  412. for (x in userOrders) {
  413. if (x.distrib != null && x.distrib.id != did) {
  414. continue;
  415. } else {
  416. for (a in x.datas) {
  417. if (a.product.id == pid) {
  418. uo = a;
  419. break;
  420. }
  421. }
  422. }
  423. }
  424. if (uo == null)
  425. throw t._("Could not find the product ::produ:: and delivery ::deliv::", {produ: pid, deliv: did});
  426. // quantity
  427. var q = 0.0;
  428. if (uo.product.hasFloatQt) {
  429. var param = StringTools.replace(qt, ",", ".");
  430. q = Std.parseFloat(param);
  431. } else {
  432. q = Std.parseInt(qt);
  433. }
  434. orders.products.push({productId: pid, quantity: q, distributionId: did});
  435. var p = db.Product.manager.get(pid, false);
  436. orders.total += p.getPrice() * q;
  437. }
  438. App.current.session.data.order = orders;
  439. // Go to payments page
  440. if (c.type == db.Contract.TYPE_CONSTORDERS) {
  441. throw Ok("/contract/order/" + c.id, t._("Your CSA order has been saved"));
  442. } else {
  443. throw Ok("/transaction/pay/", t._("In order to save your order, please choose a means of payment."));
  444. }
  445. }
  446. view.c = view.contract = c;
  447. view.userOrders = userOrders;
  448. }
  449. /**
  450. * A user edit an order for a multidistrib.
  451. */
  452. @tpl("contract/orderByDate.mtt")
  453. function doEditOrderByDate(date:Date) {
  454. if (app.user.amap.hasPayments()) {
  455. // when payments are active, the user cannot modify his order
  456. throw Redirect("/");
  457. }
  458. // cannot edit order if date is in the past
  459. if (Date.now().getTime() > date.getTime()) {
  460. var msg = t._("This delivery has already taken place, you can no longer modify the order.");
  461. if (app.user.isContractManager())
  462. msg += t._("<br/>As the manager of the contract you can modify the order from this page: <a href='/contractAdmin'>Management of contracts</a>");
  463. throw Error("/contract", msg);
  464. }
  465. // Il faut regarder le contrat de chaque produit et verifier si le contrat est toujours ouvert à la commande.
  466. var d1 = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
  467. var d2 = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 23, 59, 59);
  468. var cids = Lambda.map(app.user.amap.getActiveContracts(true), function(c) return c.id);
  469. var distribs = db.Distribution.manager.search(($contractId in cids) && $date >= d1 && $date <= d2, false);
  470. var orders = db.UserContract.manager.search($userId == app.user.id
  471. && $distributionId in Lambda.map(distribs, function(d) return d.id));
  472. view.orders = service.OrderService.prepare(orders);
  473. view.date = date;
  474. // form check
  475. if (checkToken()) {
  476. var orders_out = [];
  477. for (k in app.params.keys()) {
  478. var param = app.params.get(k);
  479. if (k.substr(0, "product".length) == "product") {
  480. // trouve le produit dans userOrders
  481. var pid = Std.parseInt(k.substr("product".length));
  482. var order = Lambda.find(orders, function(uo) return uo.product.id == pid);
  483. if (order == null)
  484. throw t._("Error, could not find the order");
  485. var q = 0.0;
  486. if (order.product.hasFloatQt) {
  487. param = StringTools.replace(param, ",", ".");
  488. q = Std.parseFloat(param);
  489. } else {
  490. q = Std.parseInt(param);
  491. }
  492. var quantity = Math.abs(q == null ? 0 : q);
  493. if (order.distribution.canOrderNow()) {
  494. // met a jour la commande
  495. var o = OrderService.edit(order, quantity);
  496. if (o != null)
  497. orders_out.push(o);
  498. }
  499. }
  500. }
  501. app.event(MakeOrder(orders_out));
  502. throw Ok("/contract", t._("Your order has been updated"));
  503. }
  504. }
  505. }