PageSourceSearch

https://seaboats.net/public/js/cart.js?v3?9375568

js seaboats.net collected 2026-09-24 22:48:57 UTC 127,088 bytes, 3,373 lines download raw bytes

1app.cartIsUpdating = false;
2app.deliveryMode = "post";
3app.debugMode = (utils.getParam("dbg") == "1");
4app.pmmEventRegistered = false;
5app.lastShipmentId = 0;
6app.lastDraftHash = 0;
7app.suppressCartNotify = false;
8
9app.updateDeliveryArea = function(newCountry, newPostCode, cb) {
10   var m = this;
11   // Store values
12   var hasChanged = false;
13   if (m.ls.deliveryCountry != newCountry) {
14      m.ls.deliveryCountry = newCountry;
15      hasChanged = true;
16   }
17
18   if (m.ls.deliveryPostCode != newPostCode) {
19      m.ls.deliveryPostCode = newPostCode;
20      hasChanged = true;
21   }
22
23   if (hasChanged) {
24      m.setLocalState(m.ls);
25   }
26
27   if (cb) {
28      cb(null, m.ls);
29   }
30   // Update all on page combos
31   // Refresh cart
32}
33
34app.cartNeedsRefresh = function(cb) {
35   if(window.disableLegacyCart){
36      if (cb) cb();
37      return;
38   }
39   var m = this;
40   //console.log("cartNeedsRefresh request");
41   if (app.refreshCartTimeout) {
42      clearTimeout(app.refreshCartTimeout);
43   }
44   
45   $('.fnc-complete-checkout').prop('disabled', true);
46   
47   app.refreshCartTimeout = setTimeout(function() {
48      if (!app.cartIsUpdating) {
49         app.cartIsUpdating = true;
50         //console.log("cartNeedsRefresh run");
51         app.calculateTotals(function(total) {
52            app.refreshCart("", function() {
53               app.saveDraftSale();
54               app.cartIsUpdating = false;
55               if (cb) cb();
56            });
57         });
58      }
59   }, 500);
60
61}
62
63app.applyDiscountCode = function(code) {
64   var m = this;
65   var validCode = false;
66   app.checkVoucherCode(code, function(data) {
67      if (data) {
68         if (data.valid) {
69            if (data.comments) {
70               app.alert("Discount Code Applied", data.comments);
71            }
72            validCode = true;
73         } else {
74            app.alert("Discount Code Error", "The code <b>" + code + "</b> is not valid or has expired.");
75         }
76      } else {
77         app.alert("Discount Code Error", "There was an error checking the code.");
78      }
79
80      // store result in sale data
81      var saleOptions = {
82         startkey: "sale_" + m.ls.saleGuid,
83         endkey: "sale_" + m.ls.saleGuid + "\uffff",
84         include_docs: true
85      };
86      m.db.allDocs(saleOptions, function(err, doc) {
87         if (doc.rows[0].doc) {
88            if (validCode) {
89               if (doc.rows[0].doc.vouchers = "") {
90                  doc.rows[0].doc.vouchers = code;
91               } else {
92                  doc.rows[0].doc.vouchers += "," + code;
93               }
94            } 
95         }
96         m.db.put(doc.rows[0].doc, {}, function(err, resp) {
97            app.cartNeedsRefresh();
98         });
99      });
100   });
101}
102
103app.selectPickerRefresh = function() {
104   try {
105      if (!utils.isBS5) {
106         if (/webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent)) {
107            //console.log("Mobile select", navigator.userAgent);
108            $('.selectpicker').selectpicker('mobile');
109         } else {
110            $('.selectpicker').selectpicker('refresh');
111         }
112      }
113   } 
114   catch (e) { }
115   //Android|
116}
117
118app.validateShipping = function(cb) {
119   var m = this;
120   var shipmentOptions = {
121      startkey: "shp_",
122      endkey: "shp_\uffff",
123      include_docs: true
124   };
125   m.db.allDocs(shipmentOptions, function(err, shipmentDoc) {
126      //debugger;
127      if (!err) {
128         if (shipmentDoc.rows.length == 0) {
129            cb("Shipping selection is required", null);
130         } else {
131            cb(null, shipmentDoc.rows.length);
132         }
133      } else {
134         cb("Shipping selection is required", null);
135      }
136   });
137}
138
139app.registerCartEvents = function() {
140   var m = this;
141   
142   $(".fnc-pkg-option-select").off().on("change", function(e) {
143      var $pkg = $($(this).parents("div.package-option"));
144      if ($(this).val().substr(0, 1) == "[") {
145         $pkg.attr("data-prd-id", "0");
146         $pkg.attr("data-pmm-id", "0");
147         $pkg.attr("data-pmm-code", "");
148         $('.selectpicker').selectpicker('refresh');
149      } else {
150         var obj = JSON.parse("{" + $(this).val() + "}");
151         $pkg.attr("data-prd-id", obj.prdId);
152         $pkg.attr("data-pmm-id", obj.pmmId);
153         $pkg.attr("data-pmm-code", obj.code);
154         $('.selectpicker').selectpicker('refresh');
155         try {
156            app.calculatePackagePrice();
157         }
158         catch(e) {}
159      }
160   });
161
162   $(".fnc-pkg-option-select").trigger('change');
163
164   var prev = null;
165   if (!app.pmmEventRegistered) {
166      $(".fnc-pmm-select").off().on("change", function(e) {
167         e.stopPropagation();
168         //if (prev) prev.style.display = "none";
169         
170         var infodata = $(this).find(":selected").data("info");
171         if (!infodata) {
172            // older combo method
173            infodata = $(this).val();
174         }
175         var obj = JSON.parse("{" + infodata + "}");
176         var pi = $(this).parents('.product-item');
177         var selPanel = $($("#pmm_" + obj.pmmId)[0]);
178         var picId = selPanel.attr("data-pic-id");
179         if (!picId) {
180            picId = pi.attr("data-pic-id");
181         }
182         
183         $(".pmm-panel[data-prd-id=" + pi.attr("data-prd-id") + "]").css("display", "none");
184         $(".default-option-price").css("display", "none");
185         
186         pi.attr("data-pmm-id", obj.pmmId);
187         pi.attr("data-prd-code", obj.code);
188         pi.attr("data-pmm-name", obj.name);
189         pi.attr("data-price", obj.price);
190         pi.attr("data-pic-id", picId);
191         pi.attr("data-discount", obj.discount);
192         pi.attr("data-original-price", obj.originalPrice);
193         selPanel.css("display", "block");
194         prev = selPanel;
195
196         $("body").trigger("productOptionChange", obj);
197         
198      });
199      app.pmmEventRegistered = true;
200   }
201   
202   if(window.disableLegacyCart){
203      return;
204   }
205
206   if (typeof cartOptions !== 'undefined') {
207      cartOptions.userHasSeenMinOrderWarning = false;
208   }
209
210   $(".fnc-cart-save-as-list").off().on("click", function(e) {
211      var el = $(this);
212      var name = prompt("Please provide a name for your list of products", "My List Name");
213      if (name) {
214         m.saveCartAsList(name, function() {
215            app.alert("List Saved", "Your list named <b>" + name + "</b> was saved successfully!");
216         });
217      } else {
218         app.alert("List Save Cancelled", "The cart contents were not saved!");
219      }
220   });
221
222   $(".fnc-cart-delete-list").off().on("click", function(e) {
223      var el = $(this);
224      var listId = el.attr("data-list-id");
225      var listName = el.attr("data-list-name");
226      app.confirm("Delete List", "Are you sure you want to delete " + listName + "?", "Yes", "No", function() {
227         var ids = listId.split("_");
228         app.deleteSavedList(ids[2], function() {
229            setTimeout(function() {
230               app.alert("List Deleted", "Your list named <b>" + listName + "</b> was deleted successfully.", function() {
231                  location.reload();
232               });
233            }, 4000);
234         })
235      });
236   });
237
238   $(".fnc-cart-quote-item-shipping").off().on("click", function(e) {
239      var el = $(this);
240      var prdId = el.attr("data-prd-id");
241      var pmmId = el.attr("data-pmm-id");
242
243      $('#dialog-confirm').on('shown.bs.modal', function() {
244         $('#dialog-confirm').off('shown.bs.modal');
245         app.loadCountryLists();
246
247         if (!m.ls.deliveryCountry) {
248            m.ls.deliveryCountry = user_country;
249         }
250
251         if (m.ls.deliveryCountry) {
252            $('#quoteDeliveryCountry option[value=' + m.ls.deliveryCountry + ']').attr('selected', 'selected');
253         }
254
255         if (m.ls.deliveryPostCode) {
256            $("#quoteDeliveryPostCode").val(m.ls.deliveryPostCode);
257         }
258
259         $('#quoteDeliveryCountry').focus();
260         app.selectPickerRefresh();
261      });
262
263      app.confirm("Shipping Quote", "Please provide your destination Country and Post Code to see available shipping options:<br><br>" +
264         "<div class='row'><div class='col-md-3'>Country:</div><div class='col-md-9'><select class='selectpicker fnc-update-shipping-country data-country-select' data-size='12' id='quoteDeliveryCountry' name='quoteDeliveryCountry'></select><br><br></div></div>" +
265         "<div class='row'><div class='col-md-3'>Post Code:</div><div class='col-md-9'><input id='quoteDeliveryPostCode' size='5' type='text'></div></div><br><br>", "Quote Shipping", "Cancel",
266         function() {
267
268            var deliveryCountry = $("#quoteDeliveryCountry").val();
269            var deliveryPostCode = $("#quoteDeliveryPostCode").val();
270
271            // console.log("Quoting for ", deliveryCountry, deliveryPostCode);
272
273            app.quoteShipping(el, deliveryCountry, deliveryPostCode, function(err, data) {
274               if (err) {
275                  alert(err);
276               } else {
277                  if (data) {
278                     var output = "<table border='0'><tr><th>Shipping Option</th><th>Price</th></tr>";
279                     for (var i = 0; i < data.items.length; i++) {
280                        var item = data.items[i];
281                        // if (item.priceCurr == m.ls.currency) {
282                        output += "<tr><td>" + item.name + "</td><td>" + item.displayPrice + "</td></tr>";
283                        //}
284                     }
285
286                     output += "</table>";
287                     setTimeout(function() {
288                        app.confirm("Shipping Options Available", output, "", "Close", function() {
289                           // all done;
290                        });
291                     }, 1000);
292                  }
293               }
294            });
295         });
296   });
297
298   $(".fnc-update-shipping").off().on("change", function(e) {
299      var el = $(this);
300      if (app.refreshShippingTimeout) {
301         clearTimeout(app.refreshShippingTimeout);
302      }
303
304      app.refreshShippingTimeout = setTimeout(function() {
305         app.updateShipping(el);
306      }, 500);
307   });
308
309
310
311   $('.fnc-cart-checkout').off("click").on("click", function(ev) {
312      $('.fnc-cart-checkout').prop('disabled', true);
313
314      if (profile.isLoggedIn()) {
315         app.applyProfileDetailsToSale('shopping-cart-details', function() {
316            $("body").fadeOut(1000);
317            window.location = "/cart/basket?_dc=" + Math.random();
318            $('.fnc-cart-checkout').prop('disabled', false);
319         });
320      } else {
321         window.location = "/cart/basket?_dc=" + Math.random();
322      }
323   });
324
325   $("#shopping-cart-details").off("submit").on("submit", function(e) {
326      e.preventDefault();
327      return false;
328   });
329
330   $(".fnc-complete-checkout").off("click").on("click", function(e) {
331      // validate sale
332      // debugger;
333      $('.fnc-complete-checkout').prop('disabled', true);
334
335      if (window.cartBypassAddressChecks) {
336         // make sure details are set on forms
337         app.applyProfileDetailsToSale('shopping-cart-details', function() {
338            // hide checkbox, show spinner
339            $('.fnc-complete-checkout i').css("display", "none");
340            $('.fnc-complete-checkout .btn-spinner').css("display", "inline-block");
341            // debugger;
342            if (app.validateCartForm("shopping-cart-details")) {
343               app.validateShipping(function(err, count) {
344                  if (err) {
345                     app.notify(err, "error");
346                  } else {
347                     app.submitSale("shopping-cart-details");   
348                  }
349               });
350            } else {
351               setTimeout(function() {
352                  $('.fnc-complete-checkout').prop('disabled', false);
353                  $('.fnc-complete-checkout .btn-spinner').css("display", "none");
354                  $('.fnc-complete-checkout i').css("display", "inline-block");
355               }, 1000);
356            }
357         });
358      } else {
359         // hide checkbox, show spinner
360         $('.fnc-complete-checkout i').css("display", "none");
361         $('.fnc-complete-checkout .btn-spinner').css("display", "inline-block");
362
363         if (app.validateCartForm("shopping-cart-details")) {
364            app.validateShipping(function(err, count) {
365               if (err) {
366                  app.notify(err, "error");
367               } else {
368                  app.submitSale("shopping-cart-details");   
369               }
370            });
371         } else {
372            setTimeout(function() {
373               $('.fnc-complete-checkout').prop('disabled', false);
374               $('.fnc-complete-checkout .btn-spinner').css("display", "none");
375               $('.fnc-complete-checkout i').css("display", "inline-block");
376            }, 1000);
377         }
378      }
379   });
380
381   $(".fnc-continue-shopping").off("click").on("click", function(e) {
382      var item=$(this);
383      if (item.attr("data-next-action")) {
384         window.location = item.attr("data-next-action");
385      } else {
386         window.history.back();
387      }
388   });
389   
390
391   $(".fnc-apply-discount-code").off("click").on("click", function(e) {
392      m.applyDiscountCode($("#cart-discount-code").val());
393   });
394
395   $('.fnc-cart-clear').off("click").on("click", function(ev) {
396      $('#dialog-clear-cart').modal('show');
397      $('.fnc-cart-clear-confirm').off().on("click", function(ev) {
398         app.cartClear();
399         $('#dialog-clear-cart').modal('hide');
400      });
401   });
402
403   if ($.TouchSpin) {
404      $(".cart-spinner").TouchSpin({
405         verticalbuttons: true,
406         verticalupclass: 'glyphicon glyphicon-plus',
407         verticaldownclass: 'glyphicon glyphicon-minus'
408      });
409   }
410
411   $(".cart-spinner").on("change", function(e) {
412      var salelnGuid = $($(this).parents(".cart-saleline")[0]).data('saleln-guid');
413      var input = $("input[name='saleln-qty-" + salelnGuid + "']");
414      app.updateSaleLine(salelnGuid, parseFloat(input.val()));
415      e.preventDefault();
416      return false;
417   });
418
419   var cartQtyChangeTimeout = null;
420   $(".cart-quantity").on("change", function(e) {
421      var salelnGuid = $($(this).parents(".cart-saleline")[0]).data('saleln-guid');
422      var input = $("input[name='saleln-qty-" + salelnGuid + "']");
423
424      if (cartQtyChangeTimeout) {
425         clearTimeout(cartQtyChangeTimeout);
426      }
427      cartQtyChangeTimeout = setTimeout(function() {
428         app.updateSaleLine(salelnGuid, Math.abs(parseFloat(input.val())));
429         clearTimeout(cartQtyChangeTimeout);
430         cartQtyChangeTimeout = null;
431      }, 500);
432   });
433
434   $('.cart-quantity').off("keyup").on("keyup", function(ev) {
435      if (ev.keyCode == 13) {
436         var salelnGuid = $($(this).parents(".cart-saleline")[0]).data('saleln-guid');
437         var input = $("input[name='saleln-qty-" + salelnGuid + "']");
438         app.updateSaleLine(salelnGuid, Math.abs(parseFloat(input.val())));
439         ev.preventDefault();
440         return false;
441      }
442   });
443
444   $(".fnc-ship-to-billing").off().on("click", function() {
445      app.setShipToAddressFromBilling();
446   });
447
448   $(".fnc-update-shipping-methods").on("input", function(e) {
449      var el = $(this);
450      //console.log("fnc-update-shipping-methods");
451      // console.log("fnc-update-shipping-methods", el);
452      //console.log(el[0].id + " input");
453      if (el[0].id == "deliveryPostcodePicker") {
454      //   console.log("top", el);
455         $("#deliveryPostcode").val(el.val());
456      }
457      //} else {
458         // fill in bottom value
459         //
460     // }
461      app.cartNeedsRefresh();
462   });
463
464   $('.fnc-update-shipping-country').on('change', function() {
465      var el = $(this);
466      //console.log("fnc-update-shipping-country", el);
467
468      m.ls.deliveryCountry = el.val();
469      //console.log("fnc-update-shipping-country deliveryCountry", m.ls.deliveryCountry);
470      if (m.ls.deliveryCountry) {
471         $('#deliveryCountry option[value=' + m.ls.deliveryCountry + ']').attr('selected', 'selected');
472      }
473      app.selectPickerRefresh();
474      app.cartNeedsRefresh();
475   });
476
477   $(".fnc-set-pickup-location").on("change", function(cmb) {
478      //console.log("fnc-set-pickup-location");
479      app.cartNeedsRefresh();
480   });
481
482   $("#cart-main").css("opacity", 1);
483   
484   if (app.pickupList) {
485      if (app.pickupList.length == 0) {
486         app.loadPickupSelectLists();
487      }
488   }
489   
490   // delivery method switch
491   $('#cart-address-panel').off().on('show.bs.collapse', function(e) {
492      switch (e.target.id) {
493         case "cart-address-delivery":
494            var options = document.querySelectorAll('#cmbPickupLocation option');
495            for (var i = 0, l = options.length; i < l; i++) {
496               if (i == 0) options[i].selected = true;
497               else options[i].selected = false;
498            }
499            app.deliveryMode = "post";
500            app.cartNeedsRefresh();
501            break;
502         case "cart-address-pickup":
503            app.deliveryMode = "pickup";
504            app.cartNeedsRefresh();
505            break;
506      }
507   });
508
509   // payment type switch
510   // 	$('.payment-panel').on('shown.bs.collapse', function (e) {
511   // 		$("#pay_type").val(e.currentTarget.id);
512   // 		console.log("Paytype: " + e.currentTarget.id);
513   // 	});
514
515   try {
516      $('.cart-user-options .collapse').collapse();
517   }
518   catch (e) {
519      
520   }
521
522   $(".fnc-cart-delete-line").off("click").on("click", function(e) {
523      if (+(e.screenX || 0) > 0) { // weird hack as Enter in discount code field triggers this
524         app.removeFromCart($(this).parents(".cart-saleline").data("saleln-guid"));
525      }
526   });
527
528   $('#cart-discount-code').off("keyup").on("keyup", function(ev) {
529      if (ev.keyCode == 13) {
530         m.applyDiscountCode($("#cart-discount-code").val());
531         ev.preventDefault();
532         return false;
533      }
534   });
535
536   
537};
538
539app.updateShipping = function(el) {
540    if(window.disableLegacyCart){
541      return;
542   }
543   var m = this;
544   //console.log("fnc-update-shipping");
545   
546   app.lastShipmentId = 0;
547   var sel = el.find("option:selected");
548   if (sel.length > 0) {
549      var opt = $(sel[0]);
550      if (opt.attr("data-spg-id")) {
551         var shipment = {
552               shpId: opt.attr("data-shp-id"),
553               spgId: opt.attr("data-spg-id"),
554               flags: opt.attr("data-flags")
555            }
556            // console.log("Update Shipment", shipment);
557            // if (app.debugMode) alert("Shp shipment:" + opt.attr("data-spg-id") + "  : " + JSON.stringify(shipment));
558         var options = {
559            startkey: "shp_",
560            endkey: "shp_\uffff",
561            include_docs: true
562         };
563         // get all shipment records
564         m.db.allDocs(options, function(err, shipments) {
565            if (!err) {
566               // loop through all shipments and set selected flag on item per group
567               for (var j = 0; j < shipments.rows.length; j++) {
568                  var shiprec = shipments.rows[j].doc;
569                  //console.log("Compare ", shiprec, shipment);
570                  if (shiprec.spgId == shipment.spgId) {
571                     shiprec.shpId = shipment.shpId;
572                     m.db.put(shiprec, function callback(err, result) {
573                        if (err) {
574                           console.log("Could not update shipment line", err);
575                        } else {
576                           app.lastShipmentId = shipment.shpId;
577                           //console.log("app.lastShipmentId = " + app.lastShipmentId);
578                        }
579                        //console.log("Updated Shipping line", result);
580
581                        app.calculateTotals();
582                     });
583                  }
584               }
585            }
586         });
587      } else {
588         //if (app.debugMode) alert("Shp 0:" + opt.attr("data-spg-id"));
589         app.calculateTotals();
590      }
591   }
592}
593
594app.addItemToCart = function(item, cb) {
595   var m = this;
596
597   var cartBtn = $('.shopping-cart');
598   for (var i = 0; i < cartBtn.length; i++) {
599      if ($($('.shopping-cart')[i]).is(":visible")) {
600         cartBtn = $($('.shopping-cart')[i]);
601         break;
602      }
603   }
604
605   if (cartBtn.length == 0) {
606      // look for alternate cart buttons
607      cartBtn = $('.cart-status');
608      for (var i = 0; i < cartBtn.length; i++) {
609         if ($($('.cart-status')[i]).is(":visible")) {
610            cartBtn = $($('.cart-status')[i]);
611            break;
612         }
613      }
614   }
615
616   m.addToCart(item, function(err, showAnimation) {
617      if (err) {
618         console.log("Add to cart error:", err);
619      } else {
620         if (cartBtn) {
621            if (!cartBtn.offset()) {
622               cartBtn = null;
623            }
624         }
625
626         if (showAnimation && cartBtn) {
627            var imgtodrag = item.parents('.product-item').find("img.product-picture").eq(0);
628
629            if (imgtodrag.length == 0) {
630               // themler image?
631               imgtodrag = item.parents('.product-item').find(".product-picture").find("img").eq(0);
632            }
633            if (imgtodrag) {
634               if (imgtodrag.offset()) {
635                  var imgclone = imgtodrag.clone()
636                     .offset({
637                        top: imgtodrag.offset().top,
638                        left: imgtodrag.offset().left
639                     })
640                     .css({
641                        'opacity': '0.8',
642                        'position': 'absolute',
643                        'max-height': '180px',
644                        'max-width': '160px',
645                        'z-index': '100'
646                     })
647                     .appendTo($('body'))
648                     .animate({
649                        'top': cartBtn.offset().top - 20,
650                        'left': cartBtn.offset().left - 20,
651                        'max-height': '50px',
652                        'max-width': '50px',
653                     }, 150, 'easeInOutExpo');
654   
655                  setTimeout(function() {
656                     cartBtn.fadeIn(100).fadeOut(100).fadeIn(100).fadeOut(100).fadeIn(100);
657                  }, 200);
658   
659                  imgclone.animate({
660                     'opacity': '0.0'
661                  }, 200, function() {
662                     $(this).detach();
663   
664                     if (cb) cb();
665                  });
666               } else {
667                  if (cb) cb();
668               }
669            }
670         } else {
671            if (cb) cb();
672         }
673      }
674   });
675}
676
677app.initCart = function() {
678   if(window.disableLegacyCart){
679      return;
680   }
681   var m = this;
682   if (!m.cartMode) { // always init the dropdown, basket init triggered separately
683      m.cartMode = "compactdropdown";
684   }
685   app.loadCountryLists();
686
687
688   $('.fnc-add-to-cart').off('click').on('click', function() {
689      var item = $(this);
690
691      var cartBtn = $('.shopping-cart');
692      for (var i = 0; i < cartBtn.length; i++) {
693         if ($($('.shopping-cart')[i]).is(":visible")) {
694            cartBtn = $($('.shopping-cart')[i]);
695            break;
696         }
697      }
698
699      if (item.attr("data-modifier-count")) {
700         // add a list of modifiers one at a time;
701         var allItems = [];
702         //debugger; 
703
704         // var product = app.resolveItemToSaleline(item);
705         var area = $(item.parents('.product-item'));
706
707         var modifiers = $(area.find(".product-modifier"));
708
709         modifiers.find("input.product-qty").each(function(index) {
710            var item = $(this);
711            if (item) {
712               //console.log("product-qty: " + index + ": " + item.val())
713               if (item.val() > 0) {
714                  allItems.push(item);
715               }
716            }
717         });
718
719         //console.log("2 Mods to add", allItems);
720         if (allItems.length > 0) {
721            var numItems = allItems.length;
722            var currentItem = 0;
723
724            var nextItem = function() {
725               if (numItems > currentItem) {
726                  var item = allItems[currentItem++];
727                  app.addItemToCart(item, nextItem);
728               } else {
729                  // all done
730                  for (var i = 0; i < allItems.length; i++) {
731                     allItems[i].val(0);
732                  }
733                  $.magnificPopup.open({
734                     items: {
735                        src: "<div class='white-popup-block' id='dlg-thanks'><h1>Items have been added to your cart</h1><br><br><p><button class='btn btn-primary fnc-continue-shopping'>Continue Shopping</button>" +
736                           "&nbsp;&nbsp;&nbsp;&nbsp;<button class='btn btn-success fnc-cart-checkout pull-right'>Go to Checkout</button></p></div>",
737                        type: 'inline'
738                     },
739                     callbacks: {
740                        open: function() {
741                           app.registerCartEvents();
742                        }
743                     }
744                  });
745               }
746            }
747            nextItem();
748         }
749         // console.log("Add Modifiers", allItems);
750      } else {
751         
752         if (item.attr("data-list-container")) {
753            // add a list of items one at a time;
754            var allItems = [];
755
756            // .in is the currently expanded list
757            $(".in #" + item.attr("data-list-container") + " input.product-qty").each(function(index) {
758               var itemQty = $(this);
759               if (itemQty) {
760                  //console.log("input.product-qty: " + index + ": " + item.val())
761                  if (itemQty.val() > 0) {
762                     allItems.push(itemQty);
763                  }
764               }
765            });
766            if (allItems.length > 0) {
767               var numItems = allItems.length;
768               var currentItem = 0;
769
770               var nextItem = function() {
771                  if (numItems > currentItem) {
772                     app.addItemToCart(allItems[currentItem++], nextItem);
773                  } else {
774                     // all done
775                     for (var i = 0; i < allItems.length; i++) {
776                        allItems[i].val(0);
777                     }
778                     $.magnificPopup.open({
779                        items: {
780                           src: "<div class='white-popup-block' id='dlg-thanks'><h1>Items have been added to your cart</h1><br><br><p><button class='btn btn-primary fnc-continue-shopping' data-next-action='" + item.attr("data-next-action") + "'>Continue Shopping</button>" +
781                              "&nbsp;&nbsp;&nbsp;&nbsp;<button class='btn btn-success fnc-cart-checkout pull-right'>Go to Checkout</button></p></div>",
782                           type: 'inline'
783                        },
784                        callbacks: {
785                           open: function() {
786                              app.registerCartEvents();
787                           }
788                        }
789                     });
790                  }
791               }
792               nextItem();
793            }
794            //console.log("Add List", allItems);
795         } else {
796            // add single item
797            app.addItemToCart(item, function() {
798               try {
799                  $.magnificPopup.close();
800               } catch (ex) {}
801            });
802         }
803      }
804   });
805
806   m.sessionDbName = 'session_' + app.ls.sessionGuid;
807   if (typeof PouchDB !== 'undefined') {
808      m.db = new PouchDB(m.sessionDbName, {
809         auto_compaction: true
810      });
811      m.db.setMaxListeners(20);
812      setTimeout(function() {
813         m.db.info(function(err, info) {
814            if (!err) {
815               m.sync();
816               m.db.changes({
817                  since: info.update_seq,
818                  continuous: false,
819                  onChange: m.refreshCart
820               });
821               m.refreshCart();
822            }
823         });
824      }, 500);
825   }
826};
827
828app.setShipToAddressFromBilling = function() {
829   var form = document.querySelector('#shopping-cart-details');
830   var addressForm = corridor(form);
831   //console.log("setShipToAddressFromBilling", addressForm)
832   if ($("#chk-ship-to-billing").attr("checked")) {
833      addressForm.deliveryFirstname = addressForm.firstname;
834      addressForm.deliveryLastname = addressForm.lastname;
835      addressForm.deliveryEmail = addressForm.email;
836      addressForm.deliveryCompany = addressForm.company;
837      addressForm.deliveryPhone = addressForm.phone;
838      addressForm.deliveryMobile = addressForm.mobile;
839      addressForm.deliveryAddress1 = addressForm.address1;
840      addressForm.deliveryAddress2 = addressForm.address2;
841      addressForm.deliveryCity = addressForm.city;
842      addressForm.deliveryState = addressForm.state;
843      addressForm.deliveryPostcode = addressForm.postcode;
844      addressForm.deliveryCountry = addressForm.country;
845   } else {
846      addressForm.deliveryFirstname = "";
847      addressForm.deliveryLastname = "";
848      addressForm.deliveryEmail = "";
849      addressForm.deliveryPhone = "";
850      addressForm.deliveryMobile = "";
851      addressForm.deliveryCompany = "";
852      addressForm.deliveryAddress1 = "";
853      addressForm.deliveryAddress2 = "";
854      addressForm.deliveryCity = "";
855      addressForm.deliveryState = "";
856      addressForm.deliveryPostcode = "";
857      addressForm.deliveryCountry = "";
858   }
859   addressForm.deliveryCountry = addressForm.deliveryCountry.trim();
860   //console.log("setShipToAddressFromBilling setting", addressForm)
861   corridor(form, addressForm);
862   app.selectPickerRefresh();
863   app.cartNeedsRefresh();
864};
865
866
867app.validateCartForm = function(formId) {
868   var m = this;
869   //console.log("validateCartForm: " + formId);
870   //debugger;
871   var cartForm = document.querySelector('#' + formId);
872   $("#" + formId + " .error").removeClass("error");
873   var cf = null;
874   var isValid = true;
875   var invalidFields = [];
876   
877   var invalidCartField = function(formId, name, note) {
878      var fld = $("#" + formId + " input[name='" + name + "']");
879      if (fld) {
880         fld.addClass("error");
881         fld.attr("title", note);
882         isValid = false;
883         invalidFields.push({name: name, note: note});
884      } else {
885         fld = $("#" + formId + " select[name='" + name + "']");
886         if (fld) {
887            fld.addClass("error");
888            fld.attr("title", note);
889            isValid = false;
890            invalidFields.push({name: name, note: note});
891         }
892      }
893   };
894
895   var validateEmail = function(value) {
896      var input = document.createElement('input');
897
898      input.type = 'email';
899      input.value = value;
900
901      return typeof input.checkValidity == 'function' ? input.checkValidity() : /\S+@\S+\.\S+/.test(value);
902   };
903
904
905   if (cartForm) {
906      cf = corridor(cartForm);
907      // check billing address
908      //console.log(JSON.stringify(cf));
909
910
911      if (cf.pay_type == "") {
912         app.notify("Please select a payment type!", "error");
913         isValid = false;
914      }
915
916
917      if (window.cartBypassAddressChecks) {
918
919         if (cf.requiredDate) {
920            //.log("cf.requiredDate", cf.requiredDate);
921         }
922
923         if (cf.custIdRequired == "1") {
924            //console.log("Override valid!");
925            if (app.ls.profile) {
926               cf.custId = app.ls.profile.custId;
927
928               // if(window.parent){
929               //  if(window.parent.cmsConfig){
930               //   cf.custId = window.parent.cmsConfig.custId;
931               //   }
932               //}
933
934               // cust ID must be set for non-verified sales
935               if (!(cf.custId > 0)) {
936                  app.notify("There is a problem with your login.  Please login again and re-try.", "error");
937                  isValid = false;
938               }
939            } else {
940               app.notify("You must be logged in to checkout.  Please login again and re-try.", "error");
941               isValid = false;
942            }
943         }
944      } else {
945
946
947         if (cf.firstname.length < 2)
948            invalidCartField(formId, "firstname", "Please enter your full first name.");
949
950         if (cf.lastname.length < 2)
951            invalidCartField(formId, "lastname", "Please enter your full last name.");
952
953         if (!validateEmail(cf.email) || cf.email.length < 3)
954            invalidCartField(formId, "email", "Please enter a valid email address.");
955
956         if (cf.address1.length < 2)
957            invalidCartField(formId, "address1", "Please enter an address.");
958
959         if (cf.phone.length < 2)
960            invalidCartField(formId, "phone", "Please enter a phone number.");
961
962         if (cf.city.length < 2)
963            invalidCartField(formId, "city", "Please enter a city name.");
964
965         if (cf.postcode.length < 4)
966            invalidCartField(formId, "postcode", "Please enter a valid post code.");
967
968         if (cf.country.length < 2)
969            invalidCartField(formId, "country", "Please choose a valid country.");
970
971         if (app.deliveryMode != "post") {
972            // pickup mode
973            var found = false;
974            for (var i = 0; i < app.pickupList.length; i++) {
975               if (app.pickupList[i].guid == cf.pickupLocation) {
976                  found = true;
977               }
978            }
979            if (!found) {
980               invalidCartField(formId, "pickupLocation", "Please choose a valid pickup location.");
981               isValid = false;
982            }
983         } else {
984            // delivery mode
985            if (cf.deliveryFirstname.length < 2)
986               invalidCartField(formId, "deliveryFirstname", "Please enter your full first name.");
987
988            if (cf.deliveryLastname.length < 2)
989               invalidCartField(formId, "deliveryLastname", "Please enter your full last name.");
990
991            if (!validateEmail(cf.deliveryEmail) || cf.deliveryEmail.length < 3)
992               invalidCartField(formId, "deliveryEmail", "Please enter a valid email address.");
993
994            if (cf.deliveryAddress1.length < 2)
995               invalidCartField(formId, "deliveryAddress1", "Please enter an address.");
996
997            if (cf.deliveryAddress1.length > 50)
998               invalidCartField(formId, "deliveryAddress1", "Address is too long.");
999
1000            if (cf.deliveryAddress2.length > 50)
1001               invalidCartField(formId, "deliveryAddress2", "Address is too long.");
1002
1003            if (cf.deliveryCity.length < 2)
1004               invalidCartField(formId, "deliveryCity", "Please enter a city name.");
1005
1006            if (cf.deliveryPhone.length < 2)
1007               invalidCartField(formId, "deliveryPhone", "Please enter a phone number.");
1008
1009            if (cf.deliveryPostcode.length < 4)
1010               invalidCartField(formId, "deliveryPostcode", "Please enter a valid post code.");
1011
1012            if (cf.deliveryCountry.length < 2) 
1013               invalidCartField(formId, "deliveryCountry", "Please choose a valid country.");
1014               
1015         }
1016
1017         // also create an account
1018         if (cf.register) {
1019            if (String(cf.password).length > 4) {
1020               if (cf.password != cf.password2) {
1021                  invalidCartField(formId, "password", "The two password fields must match.");
1022                  invalidCartField(formId, "password2", "The two password fields must match.");
1023               }
1024            } else {
1025               invalidCartField(formId, "password", "The password must be longer than 4 letters.");
1026            }
1027         }
1028         if (cartOptions.cartMinimumOrderAmount > 0) {
1029            var allowMinimumBypass = false;
1030            // CUST_CANBUYANYQTY = 131072
1031            if ((app.ls.profile.flags & 131072) == 131072) {
1032               allowMinimumBypass = true;
1033            }
1034            // console.log("cartOptions.cartMinimumOrderAmount",cartOptions.cartMinimumOrderAmount,allowMinimumBypass,app.ls.profile.flags);
1035
1036            if (!allowMinimumBypass) {
1037               if (cartOptions.cartMinimumOrderAmount > (app.ls.total.due / 10000).toFixed(2)) {
1038                  if (!cartOptions.userHasSeenMinOrderWarning) {
1039                     $.magnificPopup.open({
1040                        items: {
1041                           src: "#minimum-order-popup",
1042                           type: 'inline'
1043                        }
1044                     });
1045                     cartOptions.userHasSeenMinOrderWarning = true;
1046                     isValid = false;
1047                  }
1048
1049                  // hard stop here if the limit is enforced
1050                  if (cartOptions.cartMinimumOrderEnforceMinimum) {
1051                     isValid = false;
1052                  }
1053               }
1054            }
1055         }
1056      }
1057
1058      if (!isValid) {
1059         submitClientError({"cartErrors": JSON.stringify(invalidFields)});
1060      }
1061
1062      return isValid;
1063   }
1064   return false;
1065}
1066
1067app.updateSaleLine = function(salelnGuid, qty, updateObj) {
1068   var m = this;
1069   if (qty <= 0) {
1070      m.removeFromCart(salelnGuid);
1071   } else {
1072      m.findSaleLine({
1073         salelnGuid: salelnGuid
1074      }, function(currline) {
1075         if (qty) {
1076            currline.qty = Math.abs(parseFloat(qty));
1077         }
1078         if (updateObj) {
1079            if (updateObj.notes) {
1080               currline.notes = updateObj.notes;
1081            } else {
1082               //currline.notes = "";
1083            }
1084            currline.price = updateObj.price;
1085            currline.discount = updateObj.discount;
1086            
1087            if (m.ls.currency != updateObj.priceCurr) {
1088               // currency has changed on the server, update the current state of client
1089               m.changeCurrency(updateObj.priceCurr);
1090            }
1091            currline.priceCurr = updateObj.priceCurr;
1092         }
1093         if (currline) {
1094            currline.totalAmount = currline.qty * currline.price;
1095            currline.totalTax = ed.tax;
1096            currline.totalDiscounts = currline.qty * currline.discount;
1097            app.db.put(currline, {}, function callback(err, result) {
1098               if (!err) {
1099                  app.sync();
1100                  app.cartNeedsRefresh()
1101               } else {
1102                  console.log(err);
1103               }
1104            });
1105         }
1106      });
1107   }
1108}
1109
1110app.removeFromCart = function(salelnGuid) {
1111   var m = this;
1112
1113   // "_id": "saleline_" + m.ls.saleGuid + "_" + newSaleLnGuid
1114   var options = {
1115      startkey: "saleline_" + m.ls.saleGuid + "_" + salelnGuid,
1116      endkey: "saleline_" + m.ls.saleGuid + "_" + salelnGuid + "\uffff",
1117      include_docs: true
1118   };
1119
1120   m.db.allDocs(options, function(err, doc) {
1121      if (!err) {
1122         if (doc.total_rows >= 0) {
1123            if (doc.rows[0]) {
1124               m.db.remove(doc.rows[0].doc, function(err, item) {
1125                  m.sync();
1126                  app.cartNeedsRefresh();
1127                  m.notify("Removed " + doc.rows[0].doc.name, "info");
1128               });
1129            }
1130         } else {
1131            m.refreshCart(); 
1132         }
1133      }
1134   });
1135}
1136
1137app.addToCart = function(domEl, cb) {
1138   var m = this;
1139   var currline = null;
1140   var product = null;
1141
1142   var itemCount = 0;
1143   var subTotal = 0;
1144
1145   app.ls.cartItemCount = 1;
1146   product = m.resolveItemToSaleline(domEl);
1147
1148   product.checkoutRedirect = $(domEl).attr("data-checkout-redirect");
1149   
1150   // check that the sale is not in use Already
1151   m.checkSaleGuid(function() {
1152      console.log("addToCart", product);
1153      m.findSaleLine(product, function(currline) {
1154         if (product.unit) {
1155            switch (product.unit.toLowerCase()) {
1156               case "kg":
1157               case "g":
1158                  // don't round qty for these units.
1159                  break;
1160               default:
1161                  product.qty = Math.ceil(product.qty);
1162                  break;
1163            }
1164         }
1165         // console.log(product, currline);
1166         if (!currline) {
1167            // new line
1168            var newSaleLnGuid = app.g(0);
1169            // debugger;
1170            currline = {
1171               "_id": "saleline_" + m.ls.saleGuid + "_" + newSaleLnGuid,
1172               "contentType": "saleline",
1173               "url": window.location.hostname.replace("www.", ""),
1174               "saleGuid": m.ls.saleGuid,
1175               "salelnGuid": newSaleLnGuid,
1176               "prdId": product.prdId,
1177               "pmmId": product.pmmId,
1178               "pmiId": product.pmiId,
1179               "primaryGrpId": product.primaryGrpId,
1180               "picId": product.picId,
1181               "picFileName": product.picFileName,
1182               "schema": product.schema,
1183               "metadata": product.metadata,
1184   
1185               "shpId": product.shpId,
1186               "spgId": product.spgId,
1187               "locId": product.locId,
1188               "outletId": product.outletId,
1189   
1190               "code": product.code,
1191               "name": product.name,
1192               "type": product.type, // saleln_type
1193               "itemType": +(product.itemType || 0), // prd_type
1194               "description": product.description,
1195               "notes": product.notes,
1196               "flags": product.flags,
1197   
1198               "qty": Math.abs(parseFloat(product.qty)),
1199               "unit": product.unit,
1200               "unitQty": product.unitQty,
1201               "outerQty": product.outerQty,
1202               "moq": product.moq,
1203               "unitWeight": product.unitWeight,
1204               "weight": product.weight * product.qty,
1205   
1206               "loyaltyCost": 0,
1207               "loyaltyReward": 0,
1208               "price": product.price,
1209               "priceCurr": product.priceCurr,
1210               "priceTax": product.priceTax,
1211               "rrp": product.rrp,
1212               "discount": product.discount,
1213               "isPriceOnApplication": 0,
1214               "outerHeight": product.outerHeight || 0,
1215               "outerWidth": product.outerWidth || 0,
1216               "outerDepth": product.outerDepth || 0,
1217               "totalShipping": 0,
1218               "totalAmount": product.qty * product.price,
1219               "totalTax": parseInt(((product.qty * product.price) * (1 + (ed.tax / 100))) - (product.qty * product.price)),
1220               "totalDiscounts": product.qty * product.discount,
1221               "svcId": product.svcId
1222            }
1223         } else {
1224            currline.qty += parseFloat(product.qty);
1225            currline.weight = currline.qty * currline.unitWeight;
1226         }
1227         currline.totalAmount = currline.qty * product.price;
1228         currline.totalTax = ((currline.qty * product.price) * (1 + (ed.tax / 100))) - (currline.qty * product.price);
1229         currline.totalDiscounts = currline.qty * product.discount;
1230         currline.comment = "";
1231   
1232         var options = [];
1233         var optionError = false;
1234         $(".package-option").each(function(e) {
1235            if ($(this).attr("data-pmm-id") == 0) {
1236               $(this).css("border", "1px solid #f00");
1237               optionError = true;
1238            } else {
1239               $(this).css("border", "0px solid #f00");
1240               options.push({
1241                  prdId: $(this).attr("data-prd-id"),
1242                  pmmId: $(this).attr("data-pmm-id")
1243               });
1244               currline.comment += $(this).attr("data-pmm-code") + " ";
1245            }
1246   
1247            //console.log("Options",$(this).attr("data-prd-id"),$(this).attr("data-pmm-id"),$(this).attr("data-prd-code"));
1248         });
1249   
1250         if (!optionError) {
1251            // debugger;
1252            if (currline.itemType !== Constants.ProductTypes.PRDTYPE_SERVICE && currline.itemType !== Constants.ProductTypes.PRDTYPE_FEATURE && currline.itemType !== Constants.ProductTypes.PRDTYPE_VOUCHER) {
1253               if(window.disableLegacyCart && currline.itemType === Constants.ProductTypes.PRDTYPE_PACKAGE){ 
1254                  // create child salelines
1255                  var childLines = generatePackageSaleLines(currline, product);
1256                  if(childLines.length > 0){
1257                     m.db.bulkDocs(childLines, function callback(err, result) {
1258                        
1259                     });
1260                  }
1261                  
1262               }else{
1263                  currline.metadata = JSON.stringify(options);
1264               }
1265            }
1266   
1267            m.db.put(currline, function callback(err, result) {
1268               if (!err) {
1269                  if (!app.suppressCartNotify) {
1270                     m.notify("Added " + product.qty + " x " + product.name);
1271                  }
1272                  if (typeof fbq !== 'undefined') {
1273                     fbq('track', 'AddToCart', {
1274                       content_name: product.name, 
1275                       content_category: product.primaryGrpId,
1276                       content_ids: [product.code],
1277                       content_type: 'product',
1278                       value: product.price / 10000,
1279                       currency: product.priceCurr 
1280                     });      
1281                  }
1282                  if (typeof gtag !== 'undefined' ) {
1283                     gtag('event', 'add_to_cart', {
1284                       "items": [
1285                         {
1286                           "id": product.code,
1287                           "name": product.description,
1288                           "category": product.primaryGrpId,
1289                           "list_position": 1,
1290                           "quantity": product.qty,
1291                           "price": product.price / 10000
1292                         }
1293                       ]
1294                     });
1295                  }
1296                  if (typeof window._paq !== 'undefined') {
1297                     window._paq.push(['addEcommerceItem',
1298                       product.code, 
1299                       product.description, 
1300                       "",
1301                       product.price / 10000, 
1302                       product.qty 
1303                     ]);
1304                  }
1305                  app.cartNeedsRefresh(function() {
1306                     m.sync();
1307                  });
1308                  $("body").trigger("addToCart", product);
1309                  
1310                  if (product.checkoutRedirect == 1)  {
1311                     setTimeout(function() {
1312                        window.location = '/cart/basket';
1313                     },200);
1314                  }
1315                  
1316                  if (cb) cb(null, true);
1317               } else {
1318                  //console.log(err);
1319                  if (cb) cb("Could not update sale line", false);
1320               }
1321            });
1322   
1323            if (!m.cartTimer) {
1324               m.cartTimer = setTimeout(function() {
1325                  m.setLocalState(app.ls);
1326                  m.cartTimer = null;
1327               }, 200);
1328            }
1329         } else {
1330            app.alert("Package Options Error", "Please choose all your package options using the dropdown menus.");
1331            if (cb) cb("Please choose all your package options using the dropdown menus", false);
1332         }
1333      });      
1334   });
1335};
1336
1337app.cartClear = function(hideConfirm, cb) {
1338   var m = this;
1339   var options = {
1340      startkey: "saleline_" + m.ls.saleGuid,
1341      endkey: "saleline_" + m.ls.saleGuid + "\uffff",
1342      include_docs: true
1343   };
1344   var toDelete = [];
1345   m.db.allDocs(options, function(err, doc) {
1346      if (!err) {
1347         if (doc.rows.length >= 0) {
1348            for (var i = 0; i < doc.rows.length; i++) {
1349               if (doc.rows[i].doc.contentType == "saleline") {
1350                  doc.rows[i].doc._deleted = true;
1351                  toDelete.push(doc.rows[i].doc);
1352               }
1353            }
1354            m.db.bulkDocs(toDelete).then(function(result) {
1355               m.sync();
1356               app.resetTotals();
1357               app.cartNeedsRefresh();
1358               if (!hideConfirm) {
1359                  m.notify("All items have been removed from your cart", "info");
1360               }
1361               if (cb) cb();
1362            })
1363         } else {
1364            if (cb) cb();
1365         }
1366      } else {
1367         if (cb) cb();
1368      }
1369   })
1370};
1371
1372app.submitSale = function(formId) {
1373   var m = this;
1374   var input = document.querySelector('#' + formId);
1375   var saleForm = corridor(input);
1376
1377   // create an account?
1378   if (saleForm.register) {
1379      if (String(saleForm.password).length > 4) {
1380         profile.registerProfile(saleForm, function(err, result) {
1381            if (err) {
1382               app.notify(err, "error");
1383            }
1384            if (result) {
1385               if (result.success) {
1386                  app.notify("Your account was created.", "info");
1387                  delete saleForm.register;
1388                  app.sendSale(saleForm);
1389               } else {
1390                  app.notify(result.message, "error");
1391               }
1392            }
1393         });
1394      } else {
1395         app.notify("Password is too short", "error");
1396      }
1397   } else {
1398      app.sendSale(saleForm);
1399   }
1400};
1401
1402app.removeSaleDocs = function(cb) {
1403   var m = this;
1404
1405   var saleOptions = {
1406      startkey: "sale_" + m.ls.saleGuid,
1407      endkey: "sale_" + m.ls.saleGuid + "\uffff",
1408      include_docs: true
1409   };
1410   var saleLineOptions = {
1411      startkey: "saleline_" + m.ls.saleGuid,
1412      endkey: "saleline_" + m.ls.saleGuid + "\uffff",
1413      include_docs: true
1414   };
1415
1416
1417   m.db.allDocs(saleOptions, function(err, saleObj) {
1418      if (!err) {
1419         var toDelete = [];
1420
1421         m.db.allDocs(saleLineOptions, function(err, doc) {
1422            if (doc.rows.length >= 0) {
1423
1424               for (var i = 0; i < doc.rows.length; i++) {
1425                  if (doc.rows[i].doc.contentType == "saleline") {
1426                     doc.rows[i].doc._deleted = true;
1427                     toDelete.push(doc.rows[i].doc);
1428                  }
1429               }
1430            }
1431
1432            if (saleObj.rows.length >= 0) {
1433               for (var i = 0; i < saleObj.rows.length; i++) {
1434                  if (saleObj.rows[i].doc.contentType == "sale") {
1435                     saleObj.rows[i].doc._deleted = true;
1436                     toDelete.push(saleObj.rows[i].doc);
1437                  }
1438               }
1439            }
1440
1441            if (toDelete.length >= 0) {
1442               m.db.bulkDocs(toDelete).then(function(result) {
1443                  // console.log("All shipments have been removed from cart"); //nestsalelinesonly
1444                  if (cb) {
1445                     cb();
1446                  }
1447               });
1448            } else {
1449               if (cb) {
1450                  cb();
1451               }
1452            }
1453         });
1454      } else {
1455         if (cb) {
1456            cb();
1457         }
1458      }
1459   });
1460
1461};
1462
1463app.sendSale = function(saleForm) {
1464   var m = this;
1465   //console.log("sendSale", saleForm);
1466   $('.fnc-complete-checkout').prop('disabled', true);
1467   var saleOptions = {
1468      startkey: "sale_" + m.ls.saleGuid,
1469      endkey: "sale_" + m.ls.saleGuid + "\uffff",
1470      include_docs: true
1471   };
1472   var saleLineOptions = {
1473      startkey: "saleline_" + m.ls.saleGuid,
1474      endkey: "saleline_" + m.ls.saleGuid + "\uffff",
1475      include_docs: true
1476   };
1477   var shipmentOptions = {
1478      startkey: "shp_",
1479      endkey: "shp_\uffff",
1480      include_docs: true
1481   };
1482   m.db.allDocs(saleOptions, function(err, saleObj) {
1483      if (!err) {
1484         m.db.allDocs(saleLineOptions, function(err, doc) {
1485           // debugger;
1486            if (!err) {
1487               m.db.allDocs(shipmentOptions, function(err, shipmentDoc) {
1488                  //debugger;
1489                  if (!err) {
1490                     
1491                     var sale = saleObj.rows[0].doc;
1492                     //console.log("SendSale sale: ",sale);
1493                     if (!sale) {
1494                        sale = {}
1495                     }
1496                     
1497                     // if (sale.vouchers) {
1498                     //    sale.vouchers = [sale.vouchers];
1499                     // }
1500                     
1501                     var saleSummary = [];
1502                     
1503                     //console.log("SendSale: ", doc.rows, shipmentDoc.rows, app.pickupList, saleForm.pickupLocation )
1504                     var saleLines = [];
1505                     for (var i = 0; i < doc.rows.length; i++) {
1506                        saleLines.push(doc.rows[i].doc);
1507                        saleSummary.push({
1508                           code: doc.rows[i].doc.code,
1509                           name: doc.rows[i].doc.name,
1510                           price: doc.rows[i].doc.price,
1511                           qty: doc.rows[i].doc.qty
1512                        });
1513                     }
1514                     sale.saleLines = saleLines;
1515                     var shipmentLines = [];
1516                     for (var i = 0; i < shipmentDoc.rows.length; i++) {
1517                        shipmentLines.push(shipmentDoc.rows[i].doc);
1518                        saleSummary.push({
1519                           code: "Shipping",
1520                           name: shipmentDoc.rows[i].doc.name,
1521                           price: shipmentDoc.rows[i].doc.price,
1522                           qty: 1
1523                        });
1524                     }
1525                     sale.shipments = shipmentLines;
1526
1527                     delete saleForm.cardholder;
1528                     delete saleForm.cardnumber;
1529                     delete saleForm.mm;
1530                     delete saleForm.yy;
1531                     delete saleForm.number;
1532
1533                     
1534                     
1535                     saleForm.saleGuid = m.ls.saleGuid;
1536                     if (saleForm.requiredDate) {
1537                        saleForm.requiredDate = saleForm.requiredDate.substr(0, 10);
1538                     }
1539
1540                     // handle pickup, copy address to delivery                     
1541                     if (app.pickupList) {
1542                        if (saleForm.pickupLocation) {
1543                           if (saleForm.pickupLocation.length > 10) {
1544                              for (var i = 0; i < app.pickupList.length; i++) {
1545                                 //console.log("Pickup",saleForm.pickupLocation,app.pickupList[i]);
1546                                 if (app.pickupList[i].guid == saleForm.pickupLocation) {
1547                                    var loc = app.pickupList[i];
1548                                    saleForm.deliveryFirstname = "";
1549                                    saleForm.deliveryLastname = "";
1550                                    saleForm.deliveryCompany = saleForm.company;
1551                                    saleForm.deliveryEmail = saleForm.email;
1552                                    saleForm.deliveryPhone = loc.phone;
1553                                    saleForm.deliveryMobile = loc.mobile;
1554                                    saleForm.deliveryAddress1 = loc.address1;
1555                                    saleForm.deliveryAddress2 = loc.address2;
1556                                    saleForm.deliveryCity = loc.city;
1557                                    saleForm.deliveryPostcode = loc.postcode;
1558                                    if (loc.country == "") {
1559                                       saleForm.deliveryCountry = "NZ";
1560                                    } else {
1561                                       saleForm.deliveryCountry = loc.country.trim();
1562                                    }
1563                                 }
1564                              }
1565                              delete saleForm.pickupLocation;
1566                           }
1567                        }
1568                     }
1569
1570                     var saleRec = $.extend({}, sale, saleForm);
1571
1572                     if (m.ls.profile) {
1573                        if (m.ls.profile.custId) {
1574                           saleRec.custId = m.ls.profile.custId;
1575                        }
1576                        if (m.ls.profile.guid) {
1577                           saleRec.custGuid = m.ls.profile.guid;
1578                        }
1579                     }
1580                     saleRec.taxRate = ed.tax * 10000;
1581                     saleRec.currCode = ed.currency;
1582                     saleRec.priceLevel = ed.priceLevel;
1583                     saleRec.url = window.location.host;
1584                     saleRec.status = "ORDER";
1585                     saleRec._id = "sale_" + m.ls.saleGuid;
1586                     saleRec._rev = saleObj.rows[0].value.rev;
1587                     delete saleRec._csrf;
1588                     //console.log("Submitting", saleRec);
1589                     m.db.put(saleRec, function callback(err, result) {
1590                        if (!err) {
1591                           // app.sync(function () {
1592                           var nextUrl = "/cart/confirmation?paytype=" + saleForm.pay_type.trim().toLowerCase() + "&amount=" + (m.ls.total.due / 10000).toFixed(2) + "&ref=" + m.ls.saleGuid;
1593                           var oldSale = saleRec;
1594                           var debug = false;
1595                           var submitUrl = '/api/sale/submit';
1596                           //debugger;
1597                           if(typeof Common !== "undefined"){
1598                              if(Common.getUrlParameterByName("appDebug",window.top.location.href) === "1"){
1599                                 
1600                                 if(window.top.cmsConfig){
1601                                    submitUrl = window.top.cmsConfig.apiBaseUrl + '/api/express/saletest';
1602                                    debug = true;
1603                                    
1604                                 }
1605                              }
1606                           }
1607
1608                           // FB conversion tracking
1609                           if (typeof fbq !== 'undefined') {
1610                              try {
1611                                 var contents = [];
1612                                 for (var i = 0; i < saleSummary.length; i++) {
1613                                    contents.push({
1614                                       id: saleSummary[i].code,
1615                                       item_price: saleSummary[i].price,
1616                                       quantity: saleSummary[i].qty
1617                                    });
1618                                 }
1619                           
1620                                 fbq('track', 'Purchase',
1621                                   {
1622                                     value: (m.ls.total.due / 10000).toFixed(2),
1623                                     currency: m.ls.currency,
1624                                     contents: contents,
1625                                     content_type: 'product'
1626                                   }
1627                                 );
1628                              }
1629                              catch (ex) {
1630                                 console.log("FB conversion tracking failed");
1631                              }
1632                           }   
1633                               
1634                           // Google conversion tracking
1635                           if (typeof gtag !== 'undefined') {
1636                              try {
1637                                 var contents = [];
1638                                 for (var i = 0; i < saleSummary.length; i++) {
1639                                    contents.push({
1640                                       id: saleSummary[i].code,
1641                                       name: saleSummary[i].name,
1642                                       price: saleSummary[i].price,
1643                                       quantity: saleSummary[i].qty
1644                                    });
1645                                 }
1646                           
1647                                 gtag('event', 'purchase', {
1648                                   "value": (m.ls.total.due / 10000).toFixed(2),
1649                                   "currency": m.ls.currency,
1650                                   "items": contents
1651                                 });
1652                              }
1653                              catch (ex) {
1654                                 console.log("Google conversion tracking failed");
1655                              }
1656                           }                                  
1657                                                      
1658                           switch (saleForm.pay_type.trim().toUpperCase()) {
1659                              case "ACCOUNT":
1660                              case "TELEBANK":
1661                              case "CHEQUE":
1662                                 saleRec.payments = [{
1663                                    tenderId: Constants.transactions.paymentMethods["PAYTYPE_" + saleForm.pay_type.trim().toUpperCase()],
1664                                    saleGuid: m.ls.saleGuid,
1665                                    paidAmount: m.ls.total.due,
1666                                    state: 1
1667                                 }];
1668
1669                                 var saleData = JSON.stringify(saleRec);
1670                                 
1671                                 var data =  JSON.stringify({
1672                                    _csrf: csrf_token,
1673                                    sale: saleData
1674                                 });
1675                                    
1676                                 if(debug === true){
1677                                    data = saleData
1678                                 }
1679                                 
1680                                 var ajaxConfig = {
1681                                    url: submitUrl,
1682                                    type: 'POST',
1683                                    headers: {
1684                                       'Content-Type': 'application/json',
1685                                       'x-csrf-token': csrf_token
1686                                    },
1687                                    data: data,
1688                                    dataType: 'json',
1689                                    success: function(d) {
1690                                       // clear cart and start new sale
1691                                       
1692                                       
1693                                       
1694                                       if (d.seqNumber) {
1695                                          m.removeSaleDocs(function() {
1696                                             m.ls.saleGuid = m.g(0);
1697                                             m.ls.baseCurrency = ed.currency;
1698                                             m.ls.baseTaxRate = ed.tax;
1699
1700                                             m.resetTotals();
1701                                             m.setLocalState(m.ls);
1702
1703                                             window.location = nextUrl + "&invNumber=" + d.seqNumber; //?sid=" + app.ls.sessionGuid + "&sg=" + m.ls.saleGuid;
1704
1705                                          });
1706
1707                                       } else {
1708                                          alert("There was a problem generating a order sequence number, please try again.");
1709                                       }
1710                                    },
1711                                    failure: function() {
1712                                       alert("There was a connection problem processing your order, please reload the page and try again. ");
1713                                    }
1714                                 };
1715                                 
1716                                 if(debug === true){
1717                                    ajaxConfig.headers = {
1718                                       "Content-Type": 'application/json',
1719                                       "Session": window.top.cmsConfig.sessionGuid
1720                                    };
1721                                 }
1722                                 
1723                                 $.ajax(ajaxConfig);
1724                                 break;
1725
1726                              case "CREDITCARD":
1727                              case "PAYPAL":
1728                                 saleRec.payments = [{
1729                                    tenderId: Constants.transactions.paymentMethods["PAYTYPE_" + saleForm.pay_type.trim().toUpperCase()],
1730                                    saleGuid: m.ls.saleGuid,
1731                                    paidAmount: m.ls.total.due,
1732                                    state: 1
1733                                 }];
1734                                 var saleData = JSON.stringify(saleRec);
1735
1736                                 var data =  JSON.stringify({
1737                                    _csrf: csrf_token,
1738                                    sale: saleData
1739                                 });
1740                                    
1741                                 if(debug === true){
1742                                    data = saleData
1743                                 }
1744
1745                                 var ajaxConfig ={
1746                                    url: submitUrl,
1747                                    type: 'POST',
1748                                    headers: {
1749                                       'Content-Type': 'application/json',
1750                                       'x-csrf-token': csrf_token
1751                                    },
1752                                    data: data,
1753                                    dataType: 'json',
1754                                    success: function(d) {
1755                                       //console.log("CREDITCARD sale resp: " + d); 
1756                                       // clear cart and start new sale
1757
1758                                       if (d.seqNumber) {
1759                                          m.removeSaleDocs(function() {
1760                                             //debugger;
1761                                             var payGuid = "";
1762                                             try {
1763                                                if (d.payments[0]) {
1764                                                   payGuid = d.payments[0].payGuid;
1765                                                }
1766                                             }
1767                                             catch (e) {}
1768                                             
1769                                             nextUrl = "/function/gateway?currency=" + m.ls.currency +
1770                                                "&amount=" + (m.ls.total.due / 10000).toFixed(2) +
1771                                                "&ref=" + m.ls.saleGuid +
1772                                                "&invNumber=" + d.seqNumber +
1773                                                "&gateway=" + saleForm.pay_type.trim().toUpperCase() +
1774                                                "&firstname=" + encodeURIComponent(saleForm.firstname) +
1775                                                "&lastname=" + encodeURIComponent(saleForm.lastname) +
1776                                                "&company=" + encodeURIComponent(saleForm.company) +
1777                                                "&address1=" + encodeURIComponent(saleForm.address1) + 
1778                                                "&address2=" + encodeURIComponent(saleForm.address2) + 
1779                                                "&city=" + encodeURIComponent(saleForm.city) + 
1780                                                "&country=" + encodeURIComponent(saleForm.country) +
1781                                                "&phone=" + encodeURIComponent(saleForm.phone) +
1782                                                "&state=" + encodeURIComponent(saleForm.state) +
1783                                                "&postcode=" + encodeURIComponent(saleForm.postcode) +
1784                                                "&email=" + encodeURIComponent(saleForm.email) + 
1785                                                "&context=" + encodeURIComponent(JSON.stringify(saleSummary)) +
1786                                                "&payId=" + encodeURIComponent(payGuid);
1787
1788                                             m.ls.saleGuid = m.g(0);
1789                                             m.ls.baseCurrency = ed.currency;
1790                                             m.ls.baseTaxRate = ed.tax;
1791
1792                                             m.resetTotals();
1793                                             m.setLocalState(m.ls);
1794
1795                                             window.location = nextUrl; //?sid=" + app.ls.sessionGuid + "&sg=" + m.ls.saleGuid;
1796
1797                                          });
1798
1799                                       } else {
1800                                          alert("There was a problem processing your order, please try again.");
1801                                       }
1802                                    },
1803                                    failure: function() {
1804                                       alert("There was a problem processing your order, please try again.");
1805                                    }
1806                                 };
1807
1808                                 if(debug === true){
1809                                    ajaxConfig.headers = {
1810                                       "Content-Type": 'application/json',
1811                                       "Session": window.top.cmsConfig.sessionGuid
1812                                    };
1813                                 }
1814
1815                                 $.ajax(ajaxConfig);
1816
1817                                 // // sync again to get Inv Numbers etc
1818                                 //  app.sync(function () {
1819                                 //     var saleOptions = {
1820                                 //          startkey: "sale_" + m.ls.saleGuid,
1821                                 //          endkey: "sale_" + m.ls.saleGuid + "\uffff",
1822                                 //          include_docs: true
1823                                 //     };
1824                                 //     m.db.allDocs(saleOptions, function (err, saleObj) {
1825                                 //          // get the sale lines
1826
1827                                 //          // clear cart and start new sale
1828                                 //          m.ls.saleGuid = m.g(0);
1829                                 //          m.ls.baseCurrency = "NZD";
1830                                 //          m.ls.baseTaxRate = 0;
1831                                 //          m.resetTotals();
1832                                 //          m.setLocalState(m.ls);
1833                                 //          window.location = nextUrl; //?sid=" + app.ls.sessionGuid + "&sg=" + m.ls.saleGuid;
1834                                 //     });
1835                                 // });
1836                                 break;
1837
1838                              case "BITCOIN":
1839                                 break;
1840                              case "CONTACT":
1841                                 break;
1842
1843                           }
1844                           // });
1845
1846                        } else {
1847                           console.log(err);
1848                        }
1849                     });
1850                  }
1851               });
1852            }
1853         });
1854      } else {
1855         //app.ScriptNot
1856      }
1857   });
1858}
1859
1860app.resolveItemToSaleline = function(item) {
1861      var m = this;
1862      var group = {};
1863      var loyalty = {};
1864      var discount = {};
1865      var area = item.closest('.product-item');
1866      if (area) {
1867         var product = {
1868            prdId: +area.attr("data-prd-id") || 0,
1869            pmmId: +area.attr("data-pmm-id") || 0,
1870            code: area.attr("data-prd-code"),
1871            type: +area.attr("data-prd-type") || 0,
1872            itemType: +(area.attr("data-item-type") || 0),
1873            name: area.attr("data-item-name"),
1874            spgId: +area.attr("data-spg-id") || 0,
1875            outletId: area.attr("data-outlet-id"),
1876            locId: area.attr("data-loc-id"),
1877            picId: +area.attr("data-pic-id") || 0,
1878            picFileName: area.attr("data-pic-filename"),
1879            schema: area.attr("data-pic-path"),
1880            grpIdTree: area.attr("data-group-tree"),
1881            description: "",
1882            rrp: 0,
1883            discount: 0,
1884            price: 0,
1885            flags: +area.attr("data-flags") || 0,
1886            unitQty: 1,
1887            outerQty: 0,
1888            moq: 0,
1889            weight: +area.attr("data-weight") || 0,
1890            unitWeight: +area.attr("data-weight") || 0,
1891            outerHeight: +area.attr("data-height") || 0,
1892            outerWidth: +area.attr("data-width") || 0,
1893            outerDepth: +area.attr("data-depth") || 0,
1894            primaryGrpId: 0,
1895            unit: area.attr("data-prd-unit") || "ea",
1896            shpId: 0,
1897            notes: "",
1898            qty: 0,
1899            svcId: +area.attr("data-svc-id") || 0,
1900            metadata: area.attr("data-metadata") || ""
1901         }
1902         
1903         // any product options?
1904         var pmm = item.parents('.product-modifier');
1905         var findPmmId = pmm.attr("data-pmm-id");
1906         if (findPmmId) {
1907            product.pmmId = +findPmmId || 0;
1908            product.code = pmm.attr("data-prd-code");
1909            if (pmm.attr("data-pmm-name")) {
1910               product.name += " - " + pmm.attr("data-pmm-name");
1911            }
1912            product.weight = (+pmm.attr("data-weight") || product.weight);
1913            product.unitWeight = (+pmm.attr("data-weight") || product.weight);
1914            product.qty = Math.abs(parseFloat(+pmm.find(".product-qty").val() || 0));
1915            product.price = +pmm.attr("data-price") || 0;
1916            product.discount = Math.abs(+pmm.attr("data-discount") || 0);
1917            product.rrp = +pmm.attr("data-original-price") || 0;
1918            product.priceCurr = pmm.attr("data-cur") || ed.currency;
1919            product.priceTax = +pmm.attr("data-tax") || 0;
1920            
1921            
1922            if ((+pmm.attr("data-pic-id") || 0) > 0) {
1923               product.picId = +pmm.attr("data-pic-id") || 0;
1924               product.schema = pmm.attr("data-pic-path") || "";
1925               product.picFileName = pmm.attr("data-pic-filename");
1926            }
1927         }
1928         
1929         if (product.picId == 0) {
1930            var img = area.find("img.product-picture");
1931            if (img.length == 0) {
1932               // themler image?
1933               img = area.find(".product-picture").find("img").eq(0);
1934            }
1935            
1936            if (img.length == 0) {
1937               // div image?
1938               img = area.find(".product-picture");
1939            }
1940
1941            if (img) {
1942               product.picId = +img.attr("data-pic-id") || 0;
1943               product.schema = img.attr("data-pic-path") || "";
1944               product.picFileName = img.attr("data-pic-filename") || "";
1945            }
1946         }
1947
1948         if (area.find(".product-notes").length > 0) { 
1949            product.notes = area.find(".product-notes").val();
1950            product.description = area.find(".product-notes").val();
1951            area.find(".product-notes").val("");
1952         }
1953   
1954         if (area.find(".voucher-name").length > 0) { 
1955            console.log("Voucher name found");
1956            var voucherData = {};
1957            voucherData.name = area.find(".voucher-name").val();
1958            voucherData.email = area.find(".voucher-email").val();
1959            voucherData.message = area.find(".voucher-message").val();
1960            product.metadata = JSON.stringify(voucherData);
1961            console.log(product.metadata);
1962            
1963         }
1964            
1965         // handle variable price vouchers
1966         if (area.find(".voucher-amount").val() > 0) {
1967            product.price = area.find(".voucher-amount").val() * 10000;
1968         }
1969
1970         if (product.pmmId) {
1971            if (product.price == 0) {
1972               product.price = +area.attr("data-price") || 0;
1973               product.discount = Math.abs(+area.attr("data-discount") || 0);
1974               product.rrp = +area.attr("data-original-price") || 0;
1975            }
1976            if (product.priceCurr == "") {
1977               product.priceCurr = area.attr("data-cur") || ed.currency;
1978               product.priceTax = +area.attr("data-tax") || 0;
1979            }
1980            if (product.price == 0) {
1981               // change context to PMM level here, to get the context price
1982               area = pmm;
1983            }
1984         }
1985         
1986
1987         // if still 0 (post pmmId, normal prdId)
1988         if (product.price == 0) {
1989            var prices = area.find(".price");
1990            if (prices.length > 0) {
1991              
1991 for (var i = 0; i < prices.length; i++) {
1992                  var p = $(prices[i]);
1993                  switch (p.attr("data-type")) {
1994                     case "org":
1995                        product.rrp = +p.attr("data-amt") || 0;
1996                        break;
1997                     case "adisc": //amount disc
1998                        product.discount = Math.abs(+p.attr("data-amt") || 0);
1999                        break;
2000                     case "pdisc": //% disc
2001                        product.discount = +p.attr("data-amt") || 0;
2002                        break;
2003                     default:
2004                        product.price = +p.attr("data-amt") || 0;
2005                        break;
2006                  }
2007                  product.priceCurr = p.attr("data-cur") || ed.currency;
2008                  product.priceTax = +p.attr("data-tax") || 0;
2009               }
2010            }
2011         }
2012         
2013         // which price matrix entry is selected?
2014         var findPmiId = area.find("div.cart-price-selector");
2015         if (findPmiId) {
2016            if (findPmiId.length > 0) {
2017               var checkedOption = findPmiId.children().filter(":checked");
2018               var checkedOptionPrice = +(checkedOption.attr("data-pmi-price") || 0);
2019               product.pmiId = checkedOption.val();
2020               
2021               if (checkedOptionPrice > 0) {
2022                  product.price = checkedOptionPrice;
2023               }
2024               
2025               if (checkedOption.attr("data-pmi-title")) {
2026                  product.name += " - " + checkedOption.attr("data-pmi-title");
2027               }
2028               console.log("PMI selection: " + product.pmiId + " : " + product.name + " Price: " + product.price);
2029            }
2030         }
2031         
2032
2033         if (product.outerQty == 0) {
2034            product.outerQty = 1;
2035         }
2036         if (product.qty == 0) {
2037            if (area.context) {
2038               if (area.context.value > 0) {
2039                  product.qty = Math.abs(parseFloat(+area.context.value || 0));
2040               }
2041            }
2042         }
2043
2044         if (product.qty == 0) {
2045            // no modifier context available, use default area qty
2046            product.qty = Math.abs(parseFloat(+area.find(".product-qty").val() || 0));
2047         }
2048
2049         if (product.qty == 0) {
2050            // final default to single QTY
2051            //console.log("QTY is 0, default to 1");
2052            product.qty = 1;
2053         }
2054         product.weight = product.qty * product.unitWeight;
2055      }
2056      return product;
2057   },
2058
2059   app.findSaleLine = function(prod, cb) {
2060      var m = this;
2061      var currline = null;
2062      var options = {
2063         startkey: "saleline_" + m.ls.saleGuid,
2064         endkey: "saleline_" + m.ls.saleGuid + "\uffff",
2065         include_docs: true
2066      };
2067
2068      m.db.allDocs(options, function(err, doc) {
2069         if (!err) {
2070            if (doc.rows.length == 0) {
2071               // *** NEW SALE - add to db
2072
2073               // lookup country
2074               app.getGeoByIp(function(g) {
2075                  var country = "NZ";
2076                  var postcode = "0000";
2077                  if (g) {
2078                     if (g.geo) {
2079                        country = g.geo.country;
2080                     }
2081                     if (g.postcode) {
2082                        postcode = g.postcode.postcode;
2083                     }
2084                  }
2085
2086                  var newSale = {
2087                     _id: "sale_" + m.ls.saleGuid,
2088                     contentType: "sale",
2089                     saleGuid: m.ls.saleGuid,
2090                     status: "BASKET",
2091                     url: window.location.host,
2092                     ostId: -1, // new order
2093                     totalToPay: 0,
2094                     totalAmount: 0,
2095                     totalTax: 0,
2096                     totalShipping: 0,
2097                     totalDiscounts: 0,
2098                     loyaltyReward: 0,
2099                     loyaltyCost: 0,
2100                     deliveryCountry: country,
2101                     deliveryPostcode: postcode,
2102                     country: country,
2103                     currCode: ed.currency,
2104                     taxRate: ed.tax * 10000,
2105                     vouchers: ""
2106                  };
2107
2108                  //console.log("New Sale", newSale);
2109
2110                  m.db.put(newSale, function callback(err, result) {
2111                     if (!err) {
2112                        m.db.compact().then(function(result) {
2113                           m.db.viewCleanup().then(function(result) {
2114                              // handle result
2115                              app.applyProfileDetailsToSale();
2116                           }).catch(function(err) {
2117                              console.log(err);
2118                           });
2119                        }).catch(function(err) {
2120                           console.log(err);
2121                        });
2122                     }
2123                  });
2124               });
2125            }
2126
2127            doc.rows.forEach(function(item) {
2128               var sl = item.doc;
2129               if (sl._id.indexOf("saleline_") > -1) {
2130                  app.ls.cartItemCount++;
2131                  if (prod.salelnGuid) {
2132                     if (prod.salelnGuid == sl.salelnGuid) {
2133                        currline = item.doc;
2134                     }
2135                  }
2136                  if (sl.prdId == prod.prdId) {
2137                     if (prod.itemType != 8 && prod.itemType != 6) { // Package Type or Voucher   PRDTYPE_VOUCHER: 6,PRDTYPE_PACKAGE: 8,
2138                        if (typeof prod.taskId != "undefined") {
2139                           if (sl.taskId == prod.taskId) {
2140                              if (typeof prod.pmmId != "undefined") {
2141                                 if (sl.pmmId == prod.pmmId) {
2142                                    currline = item.doc;
2143                                 }
2144                              } else {
2145                                 // found item line
2146                                 currline = item.doc;
2147                              }
2148                           }
2149                        } else {
2150                           if (typeof prod.pmmId != "undefined") {
2151                              if (sl.pmmId == prod.pmmId) {
2152                                 currline = item.doc;
2153                              }
2154                           } else {
2155                              if (typeof prod.pmiId != "undefined") {
2156                                 if (sl.pmiId == prod.pmiId) {
2157                                    currline = item.doc;
2158                                 }
2159                              } else {
2160                                 // found item line
2161                                 currline = item.doc;
2162                              }
2163                           }
2164                        }
2165                     }
2166                  }
2167               }
2168            });
2169         }
2170         if (currline) {
2171            cb(currline);
2172         } else {
2173            // new line
2174            cb(null);
2175         }
2176      });
2177   };
2178
2179app.calculateTotals = function(cb) {
2180   var m = this;
2181   var currline = null;
2182   var lineTax = 0;
2183
2184   var saleLineOptions = {
2185      startkey: "saleline_" + m.ls.saleGuid,
2186      endkey: "saleline_" + m.ls.saleGuid + "\uffff",
2187      include_docs: true
2188   };
2189
2190   // start all at 0
2191   m.resetTotals();
2192
2193   m.db.allDocs(saleLineOptions, function(err, doc) {
2194      if (!err) {
2195         // start total off with group-wide shipping totals
2196         m.ls.total.due += m.ls.total.shipping;
2197         if (doc.total_rows == 0) {
2198            app.setLocalState(app.ls);
2199            m.renderTotals();
2200            prices.initPrices(m.ls.currency);
2201            $(".shoppingBtn").addClass("no-cart-items");
2202            cb(m.ls.total);
2203            return;
2204         }
2205         $(".shoppingBtn").removeClass("no-cart-items");
2206         
2207         doc.rows.forEach(function(item) {
2208            var sl = item.doc;
2209
2210            if (sl._id.indexOf("saleline_") > -1) {
2211               m.ls.total.amount += sl.totalAmount;
2212               lineTax = (sl.totalAmount * (1 + (ed.tax / 100))) - sl.totalAmount;
2213               m.ls.total.tax += lineTax;
2214               m.ls.total.shipping += (+sl.totalShipping || 0);
2215               m.ls.total.discounts += sl.totalDiscounts;
2216               m.ls.total.weight += (+sl.weight || 0);
2217               m.ls.total.volume += ((+sl.outerDepth || 0) * (+sl.outerWidth || 0) * (+sl.outerDepth || 0));
2218               m.ls.total.due += (sl.totalAmount + sl.totalShipping + lineTax); // - sl.totalDiscounts;
2219               
2220               m.ls.total.itemCount += sl.qty;
2221               m.ls.total.loyaltyReward += sl.loyaltyReward;
2222               m.ls.total.loyaltyCost += sl.loyaltyCost;
2223               m.ls.baseCurrency = sl.priceCurr;
2224               m.ls.baseTaxRate = ed.tax;
2225               m.ls.total.lineCount++;
2226               
2227               
2228               if (typeof window._paq !== 'undefined') {
2229                  window._paq.push(['addEcommerceItem',
2230                      sl.code, 
2231                      sl.name, 
2232                      "", 
2233                      sl.price / 10000, 
2234                      sl.qty 
2235                  ]);
2236               }
2237            }
2238         });
2239
2240         if (typeof window._paq !== 'undefined') {
2241            window._paq.push(['trackEcommerceCartUpdate', m.ls.total.amount / 10000]); 
2242         }
2243
2244         app.refreshShipping(true, function() {
2245            setTimeout(function() {
2246               // get all shipping options
2247               var shipOptions = {
2248                  startkey: "shp_",
2249                  endkey: "shp_\uffff",
2250                  include_docs: true
2251               };
2252               m.db.allDocs(shipOptions, function(err, doc) {
2253                  if (!err) {
2254                     doc.rows.forEach(function(item) {
2255                        var sl = item.doc;
2256                        m.ls.total.shipping += (+sl.price || 0);
2257                       // debugger;
2258
2259                        //                           m.ls.saleShippingTotal += (+shipment.price || 0);
2260
2261                     });
2262                     //console.log("LOOP2, m.ls.saleShippingTotal: " + m.ls.total.shipping, doc);
2263
2264                     var saleOptions = {
2265                        startkey: "sale_" + m.ls.saleGuid,
2266                        endkey: "sale_" + m.ls.saleGuid + "\uffff",
2267                        include_docs: true
2268                     };
2269                     m.db.allDocs(saleOptions, function(err, doc) {
2270                        if (doc.rows[0]) {
2271                           if (!doc.rows[0].doc.vouchers) doc.rows[0].doc.vouchers = "";
2272
2273                           var voucherDiscountAmt = 0;
2274                           var voucherDiscountTaxAmt = 0;
2275                           m.checkVoucherCode(doc.rows[0].doc.vouchers, function(data) {
2276                              if (data) {
2277                                 if (data.valid) {
2278                                    if (data.type == 2) { // %/$ off
2279                                       if (data.discount > 0) { //  % off
2280                                          voucherDiscountAmt = m.ls.total.amount - (m.ls.total.amount * ((100 - (data.discount / 10000)) / 100));
2281                                       } else { // $ off
2282                                          voucherDiscountAmt = Math.abs(data.discount);
2283                                       }
2284                                    }
2285                                    voucherDiscountTaxAmt = (voucherDiscountAmt * (1 + (ed.tax / 100))) - voucherDiscountAmt;
2286                                    m.ls.total.tax -= voucherDiscountTaxAmt;
2287                                    m.ls.total.discounts += voucherDiscountAmt;
2288                                    m.ls.total.due -= (voucherDiscountAmt + voucherDiscountTaxAmt);
2289                                    m.ls.total.amount -= voucherDiscountAmt;
2290                                    //console.log("Applied discount of: " + data.discount);
2291                                 }
2292                              }
2293
2294                              // pickup is free
2295                              if (app.deliveryMode == "pickup") {
2296                                 m.ls.total.shipping = 0;
2297                              }
2298
2299
2300                              // add on the group shipping
2301                              m.ls.total.due += m.ls.total.shipping;
2302
2303                              // add tax to shipping as well
2304                              var totalShippingTax = (m.ls.total.shipping * (1 + (ed.tax / 100))) - m.ls.total.shipping;
2305                              m.ls.total.due += totalShippingTax;
2306                              m.ls.total.tax += totalShippingTax;
2307
2308                              if (app.ls.profile) {
2309                                 if (app.ls.profile.resourceName) {
2310                                    doc.rows[0].doc.notifyEmail = app.ls.profile.resourceName;
2311                                 }
2312                              }
2313
2314                              doc.rows[0].doc.totalToPay = parseInt(m.ls.total.due);
2315                              doc.rows[0].doc.totalAmount = parseInt(m.ls.total.amount);
2316                              doc.rows[0].doc.totalTax = parseInt(m.ls.total.tax);
2317                              doc.rows[0].doc.totalShipping = parseInt((+m.ls.total.shipping || 0));
2318                              doc.rows[0].doc.totalDiscounts = parseInt(m.ls.total.discounts);
2319                              doc.rows[0].doc.loyaltyReward = parseInt(m.ls.total.loyaltyReward);
2320                              doc.rows[0].doc.loyaltyCost = parseInt(m.ls.total.loyaltyCost);
2321
2322                              if (m.ls.total.discounts > 0) {
2323                                 $(".total-discounts").css("display", "block");
2324                              }
2325
2326                              if (m.ls.profile) {
2327                                 if (m.ls.profile.custId) {
2328                                    doc.rows[0].doc.custId = m.ls.profile.custId;
2329                                 }
2330                              }
2331
2332                              m.db.put(doc.rows[0].doc, function(resp) {
2333                                 app.setLocalState(app.ls);
2334                                 m.renderTotals();
2335                                 prices.initPrices(m.ls.currency);
2336                                 app.selectPickerRefresh();
2337                                 if (cb) {
2338                                    cb(m.ls.total);
2339                                 }
2340                              });
2341                           });
2342                        } else {
2343                           if (cb) {
2344                              cb(m.ls.total);
2345                           }
2346                        }
2347                     });
2348                  }
2349               });
2350            }, 500);
2351         });
2352      } else {
2353         app.setLocalState(app.ls);
2354         m.renderTotals();
2355         prices.initPrices(m.ls.currency);
2356      }
2357   });
2358}
2359
2360app.getCurrentSale = function(cb) {
2361   var m = this;
2362   var options = {
2363      startkey: "sale_" + m.ls.saleGuid,
2364      endkey: "sale_" + m.ls.saleGuid + "\uffff",
2365      include_docs: true
2366   };
2367
2368   m.db.allDocs(options, function(err, docs) {
2369      if (docs.rows.length > 0) {
2370         cb(docs.rows[0].doc);
2371      } else {
2372         cb(null);
2373      }
2374   });
2375}
2376
2377app.checkSaleGuid = function(cb) {
2378   var m = this;
2379   $.ajax({
2380      url: '/api/sale/checksaleguid',
2381      type: 'GET',
2382      contentType: 'application/json',
2383      data: {
2384         saleGuid: m.ls.saleGuid
2385      },
2386      success: function(d) {
2387         if(d.used === true){
2388            m.removeSaleDocs(function() {
2389               m.ls.saleGuid = m.g(0);
2390               m.ls.baseCurrency = ed.currency;
2391               m.ls.baseTaxRate = ed.tax;
2392            
2393               m.resetTotals();
2394               m.setLocalState(m.ls);
2395               cb();
2396            });
2397         }else{
2398            cb();
2399         }
2400      },
2401      failure: function() {
2402         cb();
2403      }
2404   });
2405}
2406
2407app.nestSaleLinesByShippingGroup = function(sale, doc, cb) {
2408   var m = this;
2409   var data = doc.rows;
2410   var groups = {};
2411   var output = [];
2412   var groupCount = 0;
2413   var totalGroupCount = 0;
2414   var totalWithOptions = 0;
2415   var isPickup = false;
2416   var isPostCodeOk = false;
2417
2418   //	console.log("nestSaleLinesByShippingGroup");
2419   function isNumeric(n) {
2420      return !isNaN(parseFloat(n)) && isFinite(n);
2421   }
2422
2423   if (sale) {
2424      if (sale.deliveryPostcode != "") {
2425         isPostCodeOk = true;
2426      }
2427   }
2428
2429   function complete() {
2430      groupCount--;
2431      if (groupCount == 0) {
2432         if (sale) {
2433            if (sale.pickupLocation) {
2434               if (sale.pickupLocation.length > 5) {
2435                  isPickup = true;
2436               }
2437            }
2438         }
2439
2440         // console.log("nestSaleLinesByShippingGroup: " + window.cartBypassAddressChecks)
2441         if ((totalGroupCount == totalWithOptions && isPostCodeOk) || isPickup || window.cartBypassAddressChecks) {
2442            $('.fnc-complete-checkout').prop('disabled', false);
2443            $('.fnc-complete-checkout').attr("title", "");
2444         } else {
2445            //$('.fnc-complete-checkout').prop('disabled', true);
2446            $('.fnc-complete-checkout').css("pointer-events", "auto");
2447            $('.fnc-complete-checkout').attr("title", "This sale cannot be shipped to your address, please choose different options.");
2448         }
2449
2450         output.sort(function(a, b) {
2451            if (a.group.spgId < b.group.spgId) {
2452               return -1;
2453            }
2454            if (a.group.spgId > b.group.spgId) {
2455               return 1;
2456            }
2457            return 0;
2458         });
2459
2460         //console.log("SHIP out: ",output);
2461
2462         try {
2463
2464            // console.log("LOOP3 m.ls.saleShippingTotal: " + m.ls.total.shipping, doc);
2465         } catch (ex) {}
2466         cb({
2467            cart: output
2468         });
2469      }
2470   }
2471
2472   for (var i = 0; i < data.length; i++) {
2473      var row = data[i].doc;
2474      var groupValue = row["spgId"];
2475      if (groups["grp-" + groupValue] == undefined) {
2476         groups["grp-" + groupValue] = new Array();
2477      }
2478      groups["grp-" + groupValue].push(row);
2479   }
2480
2481   groupCount = Object.keys(groups).length;
2482   totalGroupCount = groupCount;
2483
2484   if (groupCount) {
2485      for (var v in groups) {
2486         if (groups[v]) {
2487            var weight = 0;
2488            var volume = 0;
2489            var lineQty = 0;
2490            var groupQty = 0;
2491
2492            for (var i = 0; i < groups[v].length; i++) {
2493               if (groups[v][i].code) {
2494                  if (groups[v][i].code.indexOf("/") > 0) {
2495                     var cmp = groups[v][i].code.split("/");
2496                     if (isNumeric(cmp[1])) {
2497                        lineQty = cmp[1]
2498                        groupQty += lineQty;
2499                     }
2500                  } else {
2501                     lineQty = groups[v][i].qty
2502                     groupQty += lineQty;
2503                  }
2504               } else {
2505                  lineQty = groups[v][i].qty
2506                  groupQty += lineQty;
2507               }
2508               weight += (groups[v][i].weight * lineQty);
2509               volume += (groups[v][i].volume * lineQty);
2510            }
2511            var grp = {
2512               spgId: groups[v][0].spgId,
2513               spgName: "",
2514               items: groups[v],
2515               weight: weight,
2516               volume: volume,
2517               qty: groupQty,
2518               isRural: false,
2519               isPickup: false
2520            }
2521
2522            app.getShippingMethods(sale, grp, function(data) {
2523               if (data) {
2524                  // console.log("getShippingMethods groupCount",groupCount);
2525                  // console.log("getShippingMethods out",data);
2526                  if (data.items.length > 0) {
2527                     totalWithOptions++;
2528                  }
2529                  output.push(data);
2530               }
2531               complete();
2532            });
2533         }
2534      }
2535   } else {
2536      cb(data);
2537   }
2538};
2539
2540app.tagSelectedOptions = function(cartInfo, cb) {
2541   var m = this;
2542
2543   var options = {
2544      startkey: "shp_",
2545      endkey: "shp_\uffff",
2546      include_docs: true
2547   };
2548   m.ls.total.shipping = 0;
2549
2550   if (cartInfo) {
2551      // get all shipment records
2552      m.db.allDocs(options, function(err, shipments) {
2553         if (!err) {
2554            // loop through all shipments and set selected flag on item per group
2555            //console.log("Shipments: ", shipments);
2556            for (var j = 0; j < shipments.rows.length; j++) {
2557               var shipment = shipments.rows[j].doc;
2558               if (cartInfo.cart) {
2559                  for (var i = 0; i < cartInfo.cart.length; i++) {
2560                     var cI = cartInfo.cart[i];
2561                     //console.log("cI", cI, shipment);
2562                     if (cI.group.spgId == shipment.spgId) {
2563                        // got the right group
2564                        var foundSelection = false;
2565                        for (var x = 0; x < cI.items.length; x++) {
2566                           var opt = cI.items[x];
2567                           //console.log("Ship Opt", opt);
2568                           if (opt.shpId == shipment.shpId) {
2569                              // have found the selected option
2570                              opt.selected = true;
2571                              m.ls.total.shipping += +(shipment.price || 0);
2572
2573                              foundSelection = true;
2574                              //console.log("* Selected Opt", shipment);
2575                           } else {
2576                              opt.selected = false;
2577                           }
2578                        }
2579                        if (!foundSelection) {
2580                           // first new option
2581                           if (cI.items[0]) {
2582                              m.ls.total.shipping += +(cI.items[0].price || 0);
2583                           }
2584                        }
2585                     }
2586                  }
2587               }
2588            }
2589            cb(cartInfo);
2590         }
2591      });
2592   } else {
2593      cb(null);
2594   }
2595}
2596
2597app.refreshCart = function(mode, cb) {
2598   var m = this;
2599   
2600   if(window.disableLegacyCart){
2601      return;
2602   }
2603
2604   var options = {
2605      startkey: "saleline_" + m.ls.saleGuid,
2606      endkey: "saleline_" + m.ls.saleGuid + "\uffff",
2607      include_docs: true
2608   };
2609
2610   if (mode) {
2611      if (mode != "nestsalelinesonly") {
2612         m.cartMode = mode;
2613      }
2614   }
2615
2616   var cartForm = document.querySelector('#shopping-cart-details');
2617   var addressForm = null;
2618   if (cartForm) {
2619      addressForm = corridor(cartForm);
2620   }
2621   m.db.allDocs(options, function(err, doc) {
2622      if (!err) {
2623         //console.log("Nesting Lines");
2624         app.nestSaleLinesByShippingGroup(addressForm, doc, function(doc) {
2625            if (mode != "nestsalelinesonly") {
2626               if (doc.cart && doc.cart[0].stockUpdates.length > 0) {
2627                  // do the updates
2628                  for (var i = 0; i < doc.cart[0].stockUpdates.length; i++) {
2629                     var itm = doc.cart[0].stockUpdates[i];
2630                     app.updateSaleLine(itm.salelnGuid, itm.newQty, itm);
2631                  }
2632                  if (cb) cb();
2633               } else {
2634                  // render cart
2635                  //console.log("tagging lines");
2636                  app.tagSelectedOptions(doc, function(doc) {
2637                     //console.log("tagSelectedOptions shp total:", m.ls.total.shipping);
2638                     console.log("tagSelectedOptions Ship Doc", doc)
2639                     if (app.deliveryMode == "pickup") {
2640                        m.ls.total.shipping = 0;
2641                     }
2642                     if (doc.cart) {
2643                        doc.cart[0].group.items.sort(function(a, b) {
2644                           if (a.code < b.code) {
2645                              return -1;
2646                           }
2647                           if (a.code > b.code) {
2648                              return 1;
2649                           }
2650                           return 0;
2651                        });
2652                     }
2653                     
2654                     dust.render("templates/components/client/cart/" + m.cartMode, doc, function(err, out) {
2655                        if (err) {
2656                           console.error(err);
2657                        } else {
2658                           $(".cart-item-list").html(out);
2659                        }
2660
2661                        app.calculateTotals(function() {
2662                           // console.log("price refresh", app.deliveryMode, m.ls.total);
2663                           //  debugger;
2664                           // if (app.deliveryMode == "pickup") {
2665                           //    m.ls.total.shipping = 0;
2666                           //}
2667                           // app.setLocalState(app.ls);
2668                           //  m.renderTotals();
2669                           //  prices.initPrices(m.ls.currency);
2670
2671                           if (addressForm) {
2672                              if (addressForm.email != "") {
2673                                 corridor(cartForm, addressForm);
2674                              }
2675                           }
2676                           app.selectPickerRefresh();
2677                           if (m.ls.total.lineCount > 0) {
2678                              $('.fnc-complete-checkout').prop('disabled', false);
2679                           } else {
2680                              $('.fnc-complete-checkout').prop('disabled', true);
2681                              $('.fnc-complete-checkout').attr("title", "There are no items in the cart. To checkout please add something first.");
2682                              //app.notify("There are no items in the cart. To checkout please add something first.", "error");
2683                           }
2684                           m.registerCartEvents();
2685
2686                           if (cb) cb();
2687                        });
2688
2689                     });
2690
2691                  });
2692               }
2693            } else {
2694               if (cb) cb();
2695            }
2696         });
2697      }
2698   });
2699}
2700
2701app.setCartFormDetailsFromSale = function(formId, callback) {
2702   if (formId) {
2703      var input = document.querySelector('#' + formId);
2704      //console.log("setCartFormDetailsFromSale", formId, input);
2705      app.getCurrentSale(function(sale) {
2706         sale.deliveryCountry = "NZ";
2707         //console.log("setCartFormDetailsFromSale setting", sale);
2708         var saleForm = corridor(input, sale);
2709         // console.log("setCartFormDetailsFromSale", sale);
2710         //$('#country').selectpicker('refresh');
2711         //$('#deliveryCountry').selectpicker('refresh');
2712         app.selectPickerRefresh();
2713         if (callback) callback();
2714      });
2715   } else {
2716      if (callback) callback();
2717   }
2718};
2719
2720app.applyProfileDetailsToSale = function(formId, callback) {
2721   var m = this;
2722 // debugger;
2723   if (window.parent) {
2724      if (window.parent.cmsConfig) {
2725         if (window.parent.cmsConfig.customerProfile) {
2726            app.ls.profile = window.parent.cmsConfig.customerProfile;
2727         }
2728      }
2729   }
2730   if (app.ls.profile) {
2731      var p = app.ls.profile;
2732
2733      // only update if legit data
2734      if (p.email) {
2735
2736         app.getCurrentSale(function(sale) {
2737            if (sale) {
2738               if (!sale.email) {
2739                  sale.email = "";
2740               }
2741               if (sale.email == "") {
2742                  sale.custId = p.custId;
2743                  sale.custGuid = p.custGuid;
2744                  //                if (p.firstname != "" && sale.firstname == "")
2745                  sale.firstname = p.firstname;
2746
2747                  //                  if (sale.lastname == "" && p.lastname != "")
2748                  sale.lastname = p.lastname;
2749
2750                  sale.name = p.name;
2751                  sale.company = p.company;
2752                  sale.email = p.email;
2753                  if (p.phone)
2754                     if (p.phone != "")
2755                        sale.phone = p.phone;
2756                     else
2757                        sale.phone = p.mobile;
2758
2759                  sale.address1 = p.address1;
2760                  sale.address2 = p.address2;
2761                  sale.city = p.city;
2762                  sale.postcode = p.postcode;
2763                  if (p.country) {
2764                     sale.country = p.country.trim();
2765                  } else {
2766                     sale.country = "NZ";
2767                  }
2768                  if (sale.country == "") {
2769                     sale.country = "NZ";
2770                  }
2771               }
2772               sale.priceLevel = p.priceLevel;
2773               if (!sale.deliveryEmail) {
2774                  sale.deliveryEmail = "";
2775               }
2776               if (sale.deliveryEmail == "") {
2777                  sale.deliveryFirstname = p.deliveryFirstname;
2778                  sale.deliveryLastname = p.deliveryLastname;
2779                  sale.deliveryCompany = p.deliveryCompany;
2780                  sale.deliveryEmail = p.deliveryEmail;
2781                  sale.deliveryPhone = p.deliveryPhone;
2782                  sale.deliveryAddress1 = p.deliveryAddress1;
2783                  sale.deliveryAddress2 = p.deliveryAddress2;
2784                  sale.deliveryCity = p.deliveryCity;
2785                  sale.deliveryPostcode = p.deliveryPostcode;
2786                  if (p.deliveryCountry) {
2787                     sale.deliveryCountry = p.deliveryCountry.trim();
2788                  } else {
2789                     sale.deliveryCountry = app.ls.geoCountry;
2790                  }
2791               }
2792
2793               app.db.put(sale, {}, function(err, resp) {
2794                  app.setLocalState(app.ls);
2795                  if (formId) {
2796                     var input = document.querySelector('#' + formId);
2797                     if (input) {
2798                        //console.log("applyProfileDetailsToSale setting", sale);
2799                        var saleForm = corridor(input, sale);
2800
2801                        //$('#country').selectpicker('refresh');
2802                        //$('#deliveryCountry').selectpicker('refresh');
2803                        app.selectPickerRefresh();
2804
2805                        if (callback) callback();
2806                        return;
2807                     } else {
2808                        if (callback) callback();
2809                        return;
2810                     }
2811                  } else {
2812                     if (callback) callback();
2813                     return;
2814                  }
2815               });
2816            } else {
2817               if (callback) callback();
2818               return;
2819            }
2820         });
2821      } else {
2822         if (callback) callback();
2823         return;
2824      }
2825   } else {
2826      if ($("#email").val() === "") {
2827         app.setCartFormDetailsFromSale("shopping-cart-details", function() {
2828            //$("#chk-ship-to-billing").attr("checked","checked");	
2829            //	app.setShipToAddressFromBilling();
2830            if (callback) callback(); //joseph added this because of double up issue
2831
2832         });
2833      } else {
2834
2835         if (callback) callback(); //joseph added this because of double up issue
2836      }
2837   }
2838
2839   //if (callback) callback(); //joseph commented this out because of double up issue. callback is being called twice, once on line 2329 and again here
2840};
2841
2842app.quoteShipping = function(domEl, deliveryCountry, deliveryPostcode, cb) {
2843
2844   var m = this;
2845   var product = m.resolveItemToSaleline(domEl);
2846   var totalAmount = parseFloat(product.qty) * product.price;
2847   var totalTax = ((parseFloat(product.qty) * product.price) * (1 + (ed.tax / 100))) - (parseFloat(product.qty) * product.price);
2848   var totalDiscounts = parseFloat(product.qty) * product.discount;
2849
2850   m.updateDeliveryArea(deliveryCountry, deliveryPostcode, function() {
2851      var obj = {
2852         "_csrf": csrf_token,
2853         "totals": {
2854            "amount": totalAmount,
2855            "tax": totalTax,
2856            "shipping": 500000,
2857            "discounts": totalDiscounts,
2858            "weight": parseFloat(product.qty) * product.weight,
2859            "volume": 0,
2860            "due": 0,
2861            "lineCount": 1,
2862            "itemCount": parseFloat(product.qty),
2863            "loyaltyReward": 0,
2864            "loyaltyCost": 0
2865         },
2866         "sale": {
2867            "pickupLocation": "",
2868            "deliveryCountry": deliveryCountry,
2869            "deliveryPostcode": deliveryPostcode
2870         },
2871         "group": {
2872            "spgId": product.spgId,
2873            "spgName": "",
2874            "items": [{
2875               "contentType": "saleline",
2876               "url": window.location.host,
2877               "saleGuid": "00000000-0000-0000-0000-000000000000",
2878               "salelnGuid": "00000000-0000-0000-0000-000000000000",
2879               "prdId": product.prdId,
2880               "pmmId": product.pmmId,
2881               "pmiId": product.pmiId,
2882               "primaryGrpId": product.prdId,
2883               "picId": product.picId,
2884               "picFileName": product.picFileName,
2885               "schema": product.schema,
2886               "metadata": "",
2887
2888               "shpId": product.shpId,
2889               "spgId": product.spgId,
2890               "locId": product.locId,
2891               "outletId": product.outletId,
2892
2893               "code": product.code,
2894               "name": product.name,
2895               "type": product.type, // saleln_type
2896               "itemType": +(product.itemType || 0), // prd_type
2897               "description": product.description,
2898               "notes": product.notes,
2899               "flags": product.flags,
2900
2901               "qty": parseFloat(product.qty),
2902               "unit": product.unit,
2903               "unitQty": product.unitQty,
2904               "outerQty": product.outerQty,
2905               "moq": product.moq,
2906               "weight": product.weight,
2907               "unitWeight": product.unitWeight,
2908               "loyaltyCost": 0,
2909               "loyaltyReward": 0,
2910               "price": product.price,
2911               "priceCurr": product.priceCurr,
2912               "priceTax": product.priceTax,
2913               "rrp": product.rrp,
2914               "discount": product.discount,
2915               "isPriceOnApplication": 0,
2916               "outerHeight": 0,
2917               "outerWidth": 0,
2918               "outerDepth": 0,
2919               "totalShipping": 0,
2920               "totalAmount": product.qty * product.price,
2921               "totalTax": ((product.qty * product.price) * (1 + (ed.tax / 100))) - (product.qty * product.price),
2922               "totalDiscounts": product.qty * product.discount
2923            }],
2924            "weight": parseFloat(product.qty) * product.unitWeight,
2925            "volume": null,
2926            "qty": parseFloat(product.qty),
2927            "isRural": false,
2928            "isPickup": false
2929         }
2930      };
2931      // console.log("quoteShipping req");
2932      $.ajax({
2933         url: '/api/shipping/methods',
2934         type: 'POST',
2935         headers: {
2936            'Content-Type': 'application/json',
2937            'x-csrf-token': csrf_token
2938         },
2939         data: JSON.stringify(obj),
2940         dataType: 'json',
2941         success: function(d) {
2942            if (cb) {
2943               cb(null, d);
2944            }
2945         },
2946         failure: function() {
2947            if (cb) {
2948               cb("An error occurred getting the available shipping options.", null);
2949            }
2950         }
2951      });
2952   });
2953};
2954
2955app.refreshShipping = function(calcOnly, cb) {
2956    if(window.disableLegacyCart){
2957      if (cb) cb();
2958      return;
2959   }
2960   var m = this;
2961   // refreshCart loads shipping options by group
2962
2963   var options = {
2964      startkey: "shp_",
2965      endkey: "shp_\uffff",
2966      include_docs: true
2967   };
2968
2969   m.ls.saleShippingTotal = 0;
2970   var toDelete = [];
2971   //alert("refreshShipping");
2972   m.db.allDocs(options, function(err, doc) {
2973      if (!err) {
2974         if (doc.rows.length >= 0) {
2975            for (var i = 0; i < doc.rows.length; i++) {
2976               if (doc.rows[i].doc.contentType == "shippingmethod") {
2977                  doc.rows[i].doc._deleted = true;
2978                  toDelete.push(doc.rows[i].doc);
2979               }
2980            }
2981            m.db.bulkDocs(toDelete).then(function(result) {
2982               //	console.log("All shipments have been removed from cart"); //nestsalelinesonly
2983               var shipments = [];
2984               var addNewShipment = true;
2985               m.refreshCart("nestsalelinesonly", function() {
2986                  setTimeout(function() {
2987                     // console.log("Reloading from current selected options");
2988                     $("#cart-main select.fnc-update-shipping").each(function(index) {
2989                        var sel = $(this);
2990                        var sel = sel.find("option:selected");
2991                        if (sel.length > 0) {
2992                           var opt = $(sel[0]);
2993                           if (opt.attr("data-spg-id")) {
2994                              var shipment = {
2995                                 _id: "shp_" + opt.attr("data-spg-id") + "_" + index,
2996                                 contentType: "shippingmethod",
2997                                 shpId: opt.attr("data-shp-id"),
2998                                 spgId: opt.attr("data-spg-id"),
2999                                 flags: opt.attr("data-flags"),
3000                                 name: opt.text(),
3001                                 price: opt.attr("data-price")
3002                              }
3003                              addNewShipment = true;
3004                              for (var i = 0; i < shipments.length; i++) {
3005                                 //alert("shipments[i].shpId: " + shipments[i].shpId + ", " + shipment.shpId);
3006                                 if (shipments[i].shpId == shipment.shpId) {
3007                                    addNewShipment = false;
3008                                 }
3009                              }
3010                              // alert("addNewShipment: " + addNewShipment + ", " + shipments.length);
3011
3012                              if (addNewShipment) {
3013                                 shipments.push(shipment);
3014
3015                                 //alert("Adding shipment" + JSON.stringify(shipment));
3016                                 //console.log("!foundExistingShipment: ", shipment);
3017                              }
3018                              app.lastShipmentId = shipment.shpId;
3019                           }
3020                        }
3021                     });
3022
3023                     m.db.bulkDocs(shipments, function(err, item) {
3024                        if (cb) cb();
3025                     });
3026                  }, 1000);
3027               });
3028            });
3029         }
3030      }
3031   })
3032};
3033
3034app.updateSaleShipment = function(spgId, shpId, shpName, quotedPrice) {
3035   var m = this;
3036
3037   var options = {
3038      startkey: "shp_" + spgId,
3039      endkey: "shp_" + spgId + "\uffff",
3040      include_docs: true
3041   };
3042   //  console.log("updateSaleShipment");
3043   m.db.allDocs(options, function(err, doc) {
3044      if (!err) {
3045         if (doc.total_rows == 0) {
3046            var shipment = {
3047               _id: "shp_" + spgId,
3048               contentType: "shippingmethod",
3049               shpId: shpId,
3050               spgId: spgId,
3051               name: shpName,
3052               price: quotedPrice
3053            }
3054         } else {
3055            var shipment = doc.rows[0].doc;
3056            shipment.shpId = shpId;
3057            shipment.spgId = spgId;
3058            shipment.name = shpName;
3059            shipment.price = quotedPrice;
3060         }
3061         m.db.put(shipment, {}, function(err, item) {
3062            m.sync();
3063            app.cartNeedsRefresh();
3064         });
3065      }
3066   });
3067}
3068
3069app.getShippingMethods = function(sale, group, cb) {
3070   var m = this;
3071   if (!sale) {
3072      sale = {}
3073   }
3074   if (!sale.pickupLocation) sale.pickupLocation = "";
3075   if (!sale.deliveryCountry) sale.deliveryCountry = "";
3076   if (!sale.deliveryPostcode) sale.deliveryPostcode = "";
3077
3078   var obj = {
3079      _csrf: csrf_token,
3080      totals: m.ls.total,
3081      sale: sale,
3082      group: group
3083   };
3084   //console.log("getShippingMethods req");
3085   //console.trace();
3086   //console.log(JSON.stringify(obj));
3087   $.ajax({
3088      url: '/api/shipping/methods',
3089      type: 'POST',
3090      headers: {
3091         'Content-Type': 'application/json',
3092         'x-csrf-token': csrf_token
3093      },
3094      data: JSON.stringify(obj),
3095      dataType: 'json',
3096      success: function(d) {
3097         if (cb) {
3098            cb(d);
3099         }
3100      },
3101      failure: function() {
3102         if (cb) {
3103            cb(null);
3104         }
3105      }
3106   });
3107}
3108
3109app.checkVoucherCode = function(voucherCode, cb) {
3110   var m = this;
3111   if (voucherCode != "") {
3112      $.ajax({
3113         url: '/api/sale/checkvoucher',
3114         type: 'POST',
3115         headers: {
3116            'Content-Type': 'application/json',
3117            'x-csrf-token': csrf_token
3118         },
3119         data: JSON.stringify({
3120            _csrf: csrf_token,
3121            voucherCode: voucherCode,
3122            totals: m.ls.total
3123         }),
3124         dataType: 'json',
3125         success: function(d) {
3126            if (cb) {
3127               cb(d);
3128            }
3129         },
3130         failure: function() {
3131            if (cb) {
3132               cb(null);
3133            }
3134         }
3135      });
3136   } else {
3137      if (cb) {
3138         cb(null);
3139      }
3140   }
3141}
3142
3143app.saveCartAsList = function(name, cb) {
3144   var m = this;
3145
3146   var options = {
3147      startkey: "saleline_" + m.ls.saleGuid,
3148      endkey: "saleline_" + m.ls.saleGuid + "\uffff",
3149      include_docs: true
3150   };
3151
3152   m.db.allDocs(options, function(err, doc) {
3153      if (!err) {
3154         var lines = [];
3155         for (var i = 0; i < doc.rows.length; i++) {
3156            var item = doc.rows[i].doc;
3157            lines.push({
3158               cOrdPrd_ID: item.prdId,
3159               cOrdPrd_Qty: item.qty,
3160               cOrdPrd_Amt: item.price / 10000,
3161               cOrdPrd_PMM: item.pmmId,
3162               cOrdPrd_Text: item.text,
3163               cOrdPrd_Type: item.type,
3164               cOrdPrd_Code: item.code,
3165               cOrdPrd_Note: item.note,
3166               cOrdPrd_GUID: item.salelnGuid,
3167               cOrdPrd_ShippingID: item.shpId,
3168               cOrdPrd_ShippingCost: item.totalShipping,
3169               cOrdPrd_MetaData: item.metadata,
3170               cOrdPrd_Discount: item.discount
3171            });
3172         }
3173         var obj = {
3174            "Description": name,
3175            "order": lines
3176         }
3177         $.ajax({
3178            url: '/account/itemlink',
3179            type: 'POST',
3180            headers: {
3181               'Content-Type': 'application/json',
3182               'x-csrf-token': csrf_token
3183            },
3184            data: JSON.stringify({
3185               _csrf: csrf_token,
3186               metadata: obj,
3187               type: "25",
3188               description: name,
3189               flags: 0,
3190               action: "save"
3191            }),
3192            dataType: 'json',
3193            success: function(d) {
3194               if (cb) {
3195                  cb(null, d);
3196               }
3197            },
3198            failure: function(d) {
3199               if (cb) {
3200                  cb("Error saving list - please try a different list name", null);
3201               }
3202            }
3203         });
3204      }
3205   });
3206}
3207
3208app.deleteSavedList = function(listId, cb) {
3209   var m = this;
3210   $.ajax({
3211      url: '/account/itemlink',
3212      type: 'POST',
3213      headers: {
3214         'Content-Type': 'application/json',
3215         'x-csrf-token': csrf_token
3216      },
3217      data: JSON.stringify({
3218         _csrf: csrf_token,
3219         itlId: listId,
3220         action: "delete"
3221      }),
3222      dataType: 'json',
3223      success: function(d) {
3224         if (cb) {
3225            cb(null, "List deleted");
3226         }
3227      },
3228      failure: function() {
3229         if (cb) {
3230            cb("Could not delete list", null);
3231         }
3232      }
3233   });
3234}
3235
3236app.saveDraftSale = function(cb) {
3237   var m = this;
3238
3239   var options = {
3240      startkey: "saleline_" + m.ls.saleGuid,
3241      endkey: "saleline_" + m.ls.saleGuid + "\uffff",
3242      include_docs: true
3243   };
3244
3245   m.db.allDocs(options, function(err, doc) {
3246      if (!err) {
3247         var lines = [];
3248         for (var i = 0; i < doc.rows.length; i++) {
3249            var item = doc.rows[i].doc;
3250            lines.push({
3251               prdId: item.prdId,
3252               qty: item.qty,
3253               pmmId: item.pmmId,
3254               pmiId: item.pmiId,
3255               text: item.text,
3256               type: item.type,
3257               code: item.code,
3258               note: item.note,
3259               meta: item.metadata
3260            });
3261         }
3262         var dataStr = JSON.stringify(lines);
3263         var currentHash = app.hashCode(dataStr);
3264         
3265         if (app.lastDraftHash != app.hashCode(dataStr)) { 
3266            app.lastDraftHash = currentHash;
3267            $.ajax({
3268               url: '/api/sale/draft',
3269               type: 'POST',
3270               headers: {
3271                  'Content-Type': 'application/json',
3272                  'x-csrf-token': csrf_token
3273               },
3274               data: JSON.stringify({
3275                  _csrf: csrf_token,
3276                  saleLines: dataStr
3277               }),
3278               dataType: 'json',
3279               success: function(d) {
3280                  if (cb) {
3281                     cb(null, d);
3282                  }
3283               },
3284               failure: function(d) {
3285                  if (cb) {
3286                     cb("Error saving draft sale", null);
3287                  }
3288               }
3289            });
3290         }
3291      }
3292   });
3293}
3294
3295app.loadDraftSale = function(cb) {
3296   var m = this;
3297
3298   $.ajax({
3299      url: '/api/sale/draft',
3300      type: 'GET',
3301      contentType: 'application/json',
3302      data: JSON.stringify({
3303         _csrf: csrf_token
3304      }),
3305      dataType: 'json',
3306      success: function(d) {
3307         if (cb) {
3308            cb(null, d);
3309         }
3310      },
3311      failure: function(d) {
3312         if (cb) {
3313            cb("Error loading draft sale", null);
3314         }
3315      }
3316   });
3317}
3318
3319app.checkCartDraftOrders = function(cb) {
3320   if (app.ls.total.itemCount == 0) {
3321      app.loadDraftSale(function(err, data) {
3322         if (err) {
3323            if (cb) {
3324               cb("Error getting draft sale", null);
3325               return;
3326            }
3327         } else {
3328            if (data.success) {
3329               var item = $('#cart-draft-sale-reload');
3330               var allItems = [];
3331   
3332               $('body').append('<div id="cart-draft-sale-reload" style="display:none">' + data.html + '</div>');
3333               $("#cart-draft-sale-reload input.product-qty").each(function(index) {
3334                  var item = $(this);
3335                  if (item) {
3336                     if (item.val() > 0) {
3337                        allItems.push(item);
3338                     }
3339                  }
3340               });
3341   
3342               if (allItems.length > 0) {
3343                  app.suppressCartNotify = true;
3344                  var numItems = allItems.length;
3345                  var currentItem = 0;
3346                  var nextItem = function() {
3347                     if (numItems > currentItem) {
3348                        var item = allItems[currentItem++];
3349                        app.addItemToCart(item, nextItem);
3350                     } else {
3351                        console.log("Draft sale with " + numItems + " restored.");
3352                        app.suppressCartNotify = false;
3353                        cb(null, "Draft sale with " + numItems + " restored.");
3354                        return;
3355                     }
3356                  }
3357                  nextItem();
3358               } else {
3359                  // no items
3360                  cb(null, "No draft sale items");
3361               }
3362            } else {
3363               cb("No data", null);
3364            }
3365         }
3366      });
3367   } else {
3368      cb("Already have cart entries", null);
3369      return;
3370   }
3371}
3372
3373app.registerCartEvents();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.