diff --git a/app/assets/javascripts/admin/directives/confirm_model_change.js.coffee b/app/assets/javascripts/admin/directives/confirm_model_change.js.coffee deleted file mode 100644 index a0b5272981..0000000000 --- a/app/assets/javascripts/admin/directives/confirm_model_change.js.coffee +++ /dev/null @@ -1,6 +0,0 @@ -angular.module("ofn.admin").directive "ofnConfirmModelChange", (ofnConfirmHandler,$timeout) -> - restrict: "A" - link: (scope, element, attrs) -> - handler = ofnConfirmHandler scope, -> scope.fetchOrders() - scope.$watch attrs.ngModel, (oldValue,newValue) -> - handler() unless oldValue == undefined || newValue == oldValue \ No newline at end of file diff --git a/app/assets/javascripts/admin/filters/select_filter.js.coffee b/app/assets/javascripts/admin/filters/select_filter.js.coffee deleted file mode 100644 index 2b03abd613..0000000000 --- a/app/assets/javascripts/admin/filters/select_filter.js.coffee +++ /dev/null @@ -1,7 +0,0 @@ -angular.module("ofn.admin").filter "selectFilter", (blankOption) -> - return (lineItems,selectedSupplier,selectedDistributor,selectedOrderCycle) -> - filtered = [] - filtered.push lineItem for lineItem in lineItems when (angular.equals(selectedSupplier,"0") || lineItem.supplier.id == selectedSupplier) && - (angular.equals(selectedDistributor,"0") || lineItem.order.distributor.id == selectedDistributor) && - (angular.equals(selectedOrderCycle,"0") || lineItem.order.order_cycle.id == selectedOrderCycle) - filtered \ No newline at end of file diff --git a/app/assets/javascripts/admin/filters/variant_filter.js.coffee b/app/assets/javascripts/admin/filters/variant_filter.js.coffee deleted file mode 100644 index ddbc0f2711..0000000000 --- a/app/assets/javascripts/admin/filters/variant_filter.js.coffee +++ /dev/null @@ -1,6 +0,0 @@ -angular.module("ofn.admin").filter "variantFilter", -> - return (lineItems,selectedUnitsProduct,selectedUnitsVariant,sharedResource) -> - filtered = [] - filtered.push lineItem for lineItem in lineItems when (angular.equals(selectedUnitsProduct,{}) || - (lineItem.units_product.id == selectedUnitsProduct.id && (sharedResource || lineItem.units_variant.id == selectedUnitsVariant.id ) ) ) - filtered \ No newline at end of file diff --git a/app/assets/javascripts/admin/index_utils/directives/datepicker.js.coffee b/app/assets/javascripts/admin/index_utils/directives/datepicker.js.coffee new file mode 100644 index 0000000000..442d9dc68b --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/directives/datepicker.js.coffee @@ -0,0 +1,9 @@ +angular.module("admin.indexUtils").directive "datepicker", -> + require: "ngModel" + link: (scope, element, attrs, ngModel) -> + element.datepicker + dateFormat: "yy-mm-dd" + onSelect: (dateText, inst) -> + scope.$apply (scope) -> + # Fires ngModel.$parsers + ngModel.$setViewValue dateText diff --git a/app/assets/javascripts/admin/index_utils/directives/select2_min_search.js.coffee b/app/assets/javascripts/admin/index_utils/directives/select2_min_search.js.coffee new file mode 100644 index 0000000000..949dba3522 --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/directives/select2_min_search.js.coffee @@ -0,0 +1,9 @@ +angular.module("admin.indexUtils").directive "select2MinSearch", -> + require: 'ngModel' + link: (scope, element, attrs, ngModel) -> + element.select2 + minimumResultsForSearch: attrs.select2MinSearch + + ngModel.$formatters.push (value) -> + if (value) + element.select2('val', value); diff --git a/app/assets/javascripts/admin/index_utils/services/blank_option.js.coffee b/app/assets/javascripts/admin/index_utils/services/blank_option.js.coffee new file mode 100644 index 0000000000..45e4de14c8 --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/services/blank_option.js.coffee @@ -0,0 +1,2 @@ +angular.module("admin.indexUtils").value "blankOption", -> + { id: "0", name: "All" } diff --git a/app/assets/javascripts/admin/index_utils/services/dereferencer.js.coffee b/app/assets/javascripts/admin/index_utils/services/dereferencer.js.coffee new file mode 100644 index 0000000000..50ce445dbf --- /dev/null +++ b/app/assets/javascripts/admin/index_utils/services/dereferencer.js.coffee @@ -0,0 +1,12 @@ +angular.module("admin.indexUtils").factory 'Dereferencer', -> + new class Dereferencer + dereference: (array, data)-> + if array + for object, i in array + array[i] = data[object.id] + + dereferenceAttr: (array, attr, data)-> + if array + for object in array + console.log attr, object[attr].id if data[object[attr].id] == undefined + object[attr] = data[object[attr].id] diff --git a/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee new file mode 100644 index 0000000000..1c58bddc95 --- /dev/null +++ b/app/assets/javascripts/admin/line_items/controllers/line_items_controller.js.coffee @@ -0,0 +1,180 @@ +angular.module("admin.lineItems").controller 'LineItemsCtrl', ($scope, $http, $q, Columns, Dereferencer, Orders, LineItems, Enterprises, OrderCycles, blankOption, VariantUnitManager) -> + $scope.loading = true + $scope.saving = false + $scope.filteredLineItems = [] + $scope.confirmDelete = true + $scope.startDate = formatDate daysFromToday -7 + $scope.endDate = formatDate daysFromToday 1 + $scope.bulkActions = [ { name: "Delete Selected", callback: $scope.deleteLineItems } ] + $scope.selectedBulkAction = $scope.bulkActions[0] + $scope.selectedUnitsProduct = {}; + $scope.selectedUnitsVariant = {}; + $scope.sharedResource = false + $scope.columns = Columns.setColumns + order_no: { name: "Order No.", visible: false } + full_name: { name: "Name", visible: true } + email: { name: "Email", visible: false } + phone: { name: "Phone", visible: false } + order_date: { name: "Order Date", visible: true } + producer: { name: "Producer", visible: true } + order_cycle: { name: "Order Cycle", visible: false } + hub: { name: "Hub", visible: false } + variant: { name: "Variant", visible: true } + quantity: { name: "Quantity", visible: true } + max: { name: "Max", visible: true } + final_weight_volume: { name: "Weight/Volume", visible: false } + price: { name: "Price", visible: false } + + $scope.confirmRefresh = -> + LineItems.allSaved() || confirm("Unsaved changes exist and will be lost if you continue.") + + $scope.refreshData = -> + $scope.loading = true + $scope.orders = Orders.index("q[state_not_eq]": "canceled", "q[completed_at_not_null]": "true", "q[completed_at_gt]": "#{$scope.startDate}", "q[completed_at_lt]": "#{$scope.endDate}") + $scope.distributors = Enterprises.index(action: "for_line_items", serializer: "basic", "q[sells_in][]": ["own", "any"] ) + $scope.orderCycles = OrderCycles.index(serializer: "basic", as: "distributor", "q[orders_close_at_gt]": "#{formatDate(daysFromToday(-90))}") + $scope.lineItems = LineItems.index("q[state_not_eq]": "canceled", "q[completed_at_not_null]": "true", "q[completed_at_gt]": "#{$scope.startDate}", "q[completed_at_lt]": "#{$scope.endDate}") + $scope.suppliers = Enterprises.index(action: "for_line_items", serializer: "basic", "q[is_primary_producer_eq]": "true" ) + + $q.all([$scope.orders.$promise, $scope.distributors.$promise, $scope.orderCycles.$promise]).then -> + Dereferencer.dereferenceAttr $scope.orders, "distributor", Enterprises.enterprisesByID + Dereferencer.dereferenceAttr $scope.orders, "order_cycle", OrderCycles.orderCyclesByID + + $q.all([$scope.orders.$promise, $scope.suppliers.$promise, $scope.lineItems.$promise]).then -> + Dereferencer.dereferenceAttr $scope.lineItems, "supplier", Enterprises.enterprisesByID + Dereferencer.dereferenceAttr $scope.lineItems, "order", Orders.ordersByID + $scope.orderCycles.unshift blankOption() + $scope.suppliers.unshift blankOption() + $scope.distributors.unshift blankOption() + $scope.resetSelectFilters() + $scope.loading = false + + + $scope.refreshData() + + $scope.submit = -> + if $scope.bulk_order_form.$valid + $scope.saving = true + $q.all(LineItems.saveAll()).then -> + $scope.saving = false + else + alert "Some errors must be resolved be before you can update orders.\nAny fields with red borders contain errors." + + $scope.deleteLineItem = (lineItem) -> + if ($scope.confirmDelete && confirm("Are you sure?")) || !$scope.confirmDelete + $http( + method: "DELETE" + url: "/api/orders/" + lineItem.order.number + "/line_items/" + lineItem.id + ).success (data) -> + $scope.lineItems.splice $scope.lineItems.indexOf(lineItem), 1 + delete LineItems.lineItemsByID[lineItem.id] + + $scope.deleteLineItems = (lineItems) -> + existingState = $scope.confirmDelete + $scope.confirmDelete = false + $scope.deleteLineItem lineItem for lineItem in lineItems when lineItem.checked + $scope.confirmDelete = existingState + + $scope.allBoxesChecked = -> + checkedCount = $scope.filteredLineItems.reduce (count,lineItem) -> + count + (if lineItem.checked then 1 else 0 ) + , 0 + checkedCount == $scope.filteredLineItems.length + + $scope.toggleAllCheckboxes = -> + changeTo = !$scope.allBoxesChecked() + lineItem.checked = changeTo for lineItem in $scope.filteredLineItems + + $scope.setSelectedUnitsVariant = (unitsProduct,unitsVariant) -> + $scope.selectedUnitsProduct = unitsProduct + $scope.selectedUnitsVariant = unitsVariant + + $scope.sumUnitValues = -> + sum = $scope.filteredLineItems.reduce (sum,lineItem) -> + sum = sum + lineItem.final_weight_volume + , 0 + + $scope.sumMaxUnitValues = -> + sum = $scope.filteredLineItems.reduce (sum,lineItem) -> + sum = sum + Math.max(lineItem.max_quantity,LineItems.pristineByID[lineItem.id].quantity) * lineItem.units_variant.unit_value + , 0 + + $scope.allFinalWeightVolumesPresent = -> + for i,lineItem of $scope.filteredLineItems + return false if !lineItem.hasOwnProperty('final_weight_volume') || !(lineItem.final_weight_volume > 0) + true + + # How is this different to OptionValueNamer#name? + # Should it be extracted to that class or VariantUnitManager? + $scope.formattedValueWithUnitName = (value, unitsProduct, unitsVariant) -> + # A Units Variant is an API object which holds unit properies of a variant + if unitsProduct.hasOwnProperty("variant_unit") && (unitsProduct.variant_unit == "weight" || unitsProduct.variant_unit == "volume") && value > 0 + scale = VariantUnitManager.getScale(value, unitsProduct.variant_unit) + Math.round(value/scale * 1000)/1000 + " " + VariantUnitManager.getUnitName(scale, unitsProduct.variant_unit) + else + '' + + $scope.fulfilled = (sumOfUnitValues) -> + # A Units Variant is an API object which holds unit properies of a variant + if $scope.selectedUnitsProduct.hasOwnProperty("group_buy_unit_size") && $scope.selectedUnitsProduct.group_buy_unit_size > 0 && + $scope.selectedUnitsProduct.hasOwnProperty("variant_unit") && + ( $scope.selectedUnitsProduct.variant_unit == "weight" || $scope.selectedUnitsProduct.variant_unit == "volume" ) + Math.round( sumOfUnitValues / $scope.selectedUnitsProduct.group_buy_unit_size * 1000)/1000 + else + '' + + $scope.unitsVariantSelected = -> + !angular.equals($scope.selectedUnitsVariant,{}) + + $scope.resetSelectFilters = -> + $scope.distributorFilter = $scope.distributors[0].id + $scope.supplierFilter = $scope.suppliers[0].id + $scope.orderCycleFilter = $scope.orderCycles[0].id + $scope.quickSearch = "" + + $scope.weightAdjustedPrice = (lineItem) -> + if lineItem.final_weight_volume > 0 + unit_value = lineItem.final_weight_volume / lineItem.quantity + pristine_unit_value = LineItems.pristineByID[lineItem.id].final_weight_volume / LineItems.pristineByID[lineItem.id].quantity + lineItem.price = LineItems.pristineByID[lineItem.id].price * (unit_value / pristine_unit_value) + + $scope.unitValueLessThanZero = (lineItem) -> + if lineItem.units_variant.unit_value <= 0 + true + else + false + + $scope.updateOnQuantity = (lineItem) -> + if lineItem.quantity > 0 + lineItem.final_weight_volume = LineItems.pristineByID[lineItem.id].final_weight_volume * lineItem.quantity / LineItems.pristineByID[lineItem.id].quantity + $scope.weightAdjustedPrice(lineItem) + + $scope.$watch "orderCycleFilter", (newVal, oldVal) -> + unless $scope.orderCycleFilter == "0" || angular.equals(newVal, oldVal) + $scope.startDate = OrderCycles.orderCyclesByID[$scope.orderCycleFilter].first_order + $scope.endDate = OrderCycles.orderCyclesByID[$scope.orderCycleFilter].last_order + +daysFromToday = (days) -> + now = new Date + now.setHours(0) + now.setMinutes(0) + now.setSeconds(0) + now.setDate( now.getDate() + days ) + now + +formatDate = (date) -> + year = date.getFullYear() + month = twoDigitNumber date.getMonth() + 1 + day = twoDigitNumber date.getDate() + return year + "-" + month + "-" + day + +formatTime = (date) -> + hours = twoDigitNumber date.getHours() + mins = twoDigitNumber date.getMinutes() + secs = twoDigitNumber date.getSeconds() + return hours + ":" + mins + ":" + secs + +twoDigitNumber = (number) -> + twoDigits = "" + number + twoDigits = ("0" + number) if number < 10 + twoDigits diff --git a/app/assets/javascripts/admin/line_items/line_items.js.coffee b/app/assets/javascripts/admin/line_items/line_items.js.coffee index afac42b5c3..dddc793895 100644 --- a/app/assets/javascripts/admin/line_items/line_items.js.coffee +++ b/app/assets/javascripts/admin/line_items/line_items.js.coffee @@ -1 +1 @@ -angular.module("admin.lineItems", ["admin.indexUtils", "admin.lineItems", "admin.enterprises", "admin.order_cycles"]) +angular.module("admin.lineItems", ["admin.indexUtils", "admin.products", "admin.orders", "admin.enterprises", "admin.order_cycles"]) diff --git a/app/assets/javascripts/admin/services/blank_option.js.coffee b/app/assets/javascripts/admin/services/blank_option.js.coffee deleted file mode 100644 index 42ff69a77b..0000000000 --- a/app/assets/javascripts/admin/services/blank_option.js.coffee +++ /dev/null @@ -1,2 +0,0 @@ -angular.module("ofn.admin").value "blankOption", -> - { id: "0", name: "All" } \ No newline at end of file diff --git a/spec/javascripts/unit/admin/line_items/controllers/line_items_controller_spec.js.coffee b/spec/javascripts/unit/admin/line_items/controllers/line_items_controller_spec.js.coffee new file mode 100644 index 0000000000..dbe9ead432 --- /dev/null +++ b/spec/javascripts/unit/admin/line_items/controllers/line_items_controller_spec.js.coffee @@ -0,0 +1,332 @@ +describe "AdminOrderMgmtCtrl", -> + ctrl = scope = httpBackend = VariantUnitManager = Enterprises = Orders = LineItems = OrderCycles = null + supplier = distributor = orderCycle = null + + beforeEach -> + module "admin.lineItems" + + this.addMatchers + toDeepEqual: (expected) -> + return angular.equals(this.actual, expected) + + beforeEach inject(($controller, $rootScope, $httpBackend, _VariantUnitManager_, _Enterprises_, _Orders_, _LineItems_, _OrderCycles_) -> + scope = $rootScope.$new() + ctrl = $controller + httpBackend = $httpBackend + Enterprises = _Enterprises_ + Orders = _Orders_ + LineItems = _LineItems_ + OrderCycles = _OrderCycles_ + VariantUnitManager = _VariantUnitManager_ + spyOn(window, "formatDate").andReturn "SomeDate" + + supplier = { id: 1, name: "Supplier" } + distributor = { id: 5, name: "Distributor" } + orderCycle = { id: 4, name: "OC1" } + order = { id: 9, order_cycle: { id: 4 }, distributor: { id: 5 }, number: "R123456" } + lineItem = { id: 7, quantity: 3, order: { id: 9 }, supplier: { id: 1 } } + + httpBackend.expectGET("/admin/orders.json?q%5Bcompleted_at_gt%5D=SomeDate&q%5Bcompleted_at_lt%5D=SomeDate&q%5Bcompleted_at_not_null%5D=true&q%5Bstate_not_eq%5D=canceled").respond [order] + httpBackend.expectGET("/admin/enterprises/for_line_items.json?q%5Bsells_in%5D%5B%5D=own&q%5Bsells_in%5D%5B%5D=any&serializer=basic").respond [distributor] + httpBackend.expectGET("/admin/order_cycles.json?as=distributor&q%5Borders_close_at_gt%5D=SomeDate&serializer=basic").respond [orderCycle] + httpBackend.expectGET("/admin/line_items.json?q%5Bcompleted_at_gt%5D=SomeDate&q%5Bcompleted_at_lt%5D=SomeDate&q%5Bcompleted_at_not_null%5D=true&q%5Bstate_not_eq%5D=canceled").respond [lineItem] + httpBackend.expectGET("/admin/enterprises/for_line_items.json?q%5Bis_primary_producer_eq%5D=true&serializer=basic").respond [supplier] + + ctrl "LineItemsCtrl", {$scope: scope, Enterprises: Enterprises, Orders: Orders, LineItems: LineItems, OrderCycles: OrderCycles} + httpBackend.flush() + ) + + describe "initialisation", -> + it "gets suppliers, adds a blank option as the first in the list", -> + expect(scope.suppliers).toDeepEqual [ { id : '0', name : 'All' }, supplier ] + + it "gets distributors, adds a blank option as the first in the list", -> + expect(scope.distributors).toDeepEqual [ { id : '0', name : 'All' }, distributor ] + + it "stores enterprises in an list that is accessible by id", -> + expect(Enterprises.enterprisesByID[1]).toDeepEqual supplier + + it "gets order cycles, adds a blank option as the first in the list", -> + expect(scope.orderCycles).toDeepEqual [ { id : '0', name : 'All' }, orderCycle ] + + it "gets orders, with dereferenced order cycles and distributors", -> + expect(scope.orders).toDeepEqual [ { id: 9, order_cycle: orderCycle, distributor: distributor, number: "R123456" } ] + + it "gets line_items, with dereferenced orders and suppliers", -> + expect(scope.lineItems).toDeepEqual [ { id: 7, quantity: 3, order: scope.orders[0], supplier: supplier } ] + + it "resets the select filters", -> + expect(scope.distributorFilter).toBe '0' + expect(scope.supplierFilter).toBe '0' + expect(scope.orderCycleFilter).toBe '0' + expect(scope.quickSearch).toBe = "" + + it "sets loading to false", -> + expect(scope.loading).toBe false + + describe "deleting a line item", -> + order = line_item1 = line_item2 = null + + beforeEach -> + spyOn(window,"confirm").andReturn true + order = { number: "R12345678", line_items: [] } + line_item1 = { id: 1, order: order } + line_item2 = { id: 2, order: order } + order.line_items = [ line_item1, line_item2 ] + + it "sends a delete request via the API", -> + httpBackend.expectDELETE("/api/orders/#{line_item1.order.number}/line_items/#{line_item1.id}").respond "nothing" + scope.deleteLineItem line_item1 + httpBackend.flush() + + it "does not remove line_item from the line_items array when request is not successful", -> + httpBackend.expectDELETE("/api/orders/#{line_item1.order.number}/line_items/#{line_item1.id}").respond 404, "NO CONTENT" + scope.deleteLineItem line_item1 + httpBackend.flush() + expect(order.line_items).toEqual [line_item1, line_item2] + + describe "deleting 'checked' line items", -> + line_item1 = line_item2 = line_item3 = line_item4 = null + + beforeEach -> + line_item1 = { name: "line item 1", checked: false } + line_item2 = { name: "line item 2", checked: true } + line_item3 = { name: "line item 3", checked: false } + line_item4 = { name: "line item 4", checked: true } + scope.lineItems = [ line_item1, line_item2, line_item3, line_item4 ] + + it "calls deletedLineItem for each 'checked' line item", -> + spyOn(scope, "deleteLineItem") + scope.deleteLineItems(scope.lineItems) + expect(scope.deleteLineItem).toHaveBeenCalledWith(line_item2) + expect(scope.deleteLineItem).toHaveBeenCalledWith(line_item4) + expect(scope.deleteLineItem).not.toHaveBeenCalledWith(line_item1) + expect(scope.deleteLineItem).not.toHaveBeenCalledWith(line_item3) + + describe "check boxes for line items", -> + line_item1 = line_item2 = null + + beforeEach -> + line_item1 = { name: "line item 1", checked: false } + line_item2 = { name: "line item 2", checked: false } + scope.filteredLineItems = [ line_item1, line_item2 ] + + it "keeps track of whether all filtered lines items are 'checked' or not", -> + expect(scope.allBoxesChecked()).toEqual false + line_item1.checked = true + expect(scope.allBoxesChecked()).toEqual false + line_item2.checked = true + expect(scope.allBoxesChecked()).toEqual true + line_item1.checked = false + expect(scope.allBoxesChecked()).toEqual false + + it "toggles the 'checked' attribute of all line items based to the value of allBoxesChecked", -> + scope.toggleAllCheckboxes() + expect(scope.allBoxesChecked()).toEqual true + line_item1.checked = false + expect(scope.allBoxesChecked()).toEqual false + scope.toggleAllCheckboxes() + expect(scope.allBoxesChecked()).toEqual true + scope.toggleAllCheckboxes() + expect(scope.allBoxesChecked()).toEqual false + + describe "unit calculations", -> + describe "fulfilled()", -> + it "returns '' if selectedUnitsVariant has no property 'variant_unit'", -> + expect(scope.fulfilled()).toEqual '' + + it "returns '' if selectedUnitsVariant has no property 'group_buy_unit_size' or group_buy_unit_size is 0", -> + scope.selectedUnitsProduct = { variant_unit: "weight", group_buy_unit_size: 0 } + expect(scope.fulfilled()).toEqual '' + scope.selectedUnitsProduct = { variant_unit: "weight" } + expect(scope.fulfilled()).toEqual '' + + it "returns '', and does not call Math.round if variant_unit is 'items'", -> + spyOn(Math,"round") + scope.selectedUnitsProduct = { variant_unit: "items", group_buy_unit_size: 10 } + expect(scope.fulfilled()).toEqual '' + expect(Math.round).not.toHaveBeenCalled() + + it "calls Math.round() if variant_unit is 'weight' or 'volume'", -> + spyOn(Math,"round") + scope.selectedUnitsProduct = { variant_unit: "weight", group_buy_unit_size: 10 } + scope.fulfilled() + expect(Math.round).toHaveBeenCalled() + scope.selectedUnitsProduct = { variant_unit: "volume", group_buy_unit_size: 10 } + scope.fulfilled() + expect(Math.round).toHaveBeenCalled() + + it "returns the quantity of fulfilled group buy units", -> + scope.selectedUnitsProduct = { variant_unit: "weight", group_buy_unit_size: 1000 } + expect(scope.fulfilled(1500)).toEqual 1.5 + + describe "allFinalWeightVolumesPresent()", -> + it "returns false if the unit_value of any item in filteredLineItems does not exist", -> + scope.filteredLineItems = [ + { final_weight_volume: 1000 } + { final_weight_volume: 3000 } + { final_weight_yayaya: 2000 } + ] + expect(scope.allFinalWeightVolumesPresent()).toEqual false + + it "returns false if the unit_value of any item in filteredLineItems is not a number greater than 0", -> + scope.filteredLineItems = [ + { final_weight_volume: 0 } + { final_weight_volume: 3000 } + { final_weight_volume: 2000 } + ] + expect(scope.allFinalWeightVolumesPresent()).toEqual false + scope.filteredLineItems = [ + { final_weight_volume: 'lalala' } + { final_weight_volume: 3000 } + { final_weight_volume: 2000 } + ] + expect(scope.allFinalWeightVolumesPresent()).toEqual false + + it "returns true if the unit_value of all items in filteredLineItems are numbers greater than 0", -> + scope.filteredLineItems = [ + { final_weight_volume: 1000 } + { final_weight_volume: 3000 } + { final_weight_volume: 2000 } + ] + expect(scope.allFinalWeightVolumesPresent()).toEqual true + + describe "sumUnitValues()", -> + it "returns the sum of the final_weight_volumes line_items", -> + scope.filteredLineItems = [ + { final_weight_volume: 2 } + { final_weight_volume: 7 } + { final_weight_volume: 21 } + ] + expect(scope.sumUnitValues()).toEqual 30 + + describe "sumMaxUnitValues()", -> + it "returns the sum of the product of unit_value and maxOf(max_quantity, pristine quantity) for specified line_items", -> + LineItems.pristineByID = { 1: { quantity: 2 }, 2: { quantity: 3 }, 3: { quantity: 7 } } + scope.filteredLineItems = [ + { id: 1, units_variant: { unit_value: 1 }, max_quantity: 5 } + { id: 2, units_variant: { unit_value: 2 }, max_quantity: 1 } + { id: 3, units_variant: { unit_value: 3 }, max_quantity: 10 } + ] + sp0 = scope.filteredLineItems[0].units_variant.unit_value * Math.max(LineItems.pristineByID[scope.filteredLineItems[0].id].quantity, scope.filteredLineItems[0].max_quantity) + sp1 = scope.filteredLineItems[1].units_variant.unit_value * Math.max(LineItems.pristineByID[scope.filteredLineItems[1].id].quantity, scope.filteredLineItems[1].max_quantity) + sp2 = scope.filteredLineItems[2].units_variant.unit_value * Math.max(LineItems.pristineByID[scope.filteredLineItems[2].id].quantity, scope.filteredLineItems[2].max_quantity) + + expect(scope.sumMaxUnitValues()).toEqual (sp0 + sp1 + sp2) + + describe "formatting a value based upon the properties of a specified Units Variant", -> + # A Units Variant is an API object which holds unit properies of a variant + + beforeEach -> + spyOn(Math,"round").andCallThrough() + + it "returns '' if selectedUnitsVariant has no property 'variant_unit'", -> + expect(scope.formattedValueWithUnitName(1,{})).toEqual '' + + it "returns '', and does not call Math.round if variant_unit is 'items'", -> + unitsVariant = { variant_unit: "items" } + expect(scope.formattedValueWithUnitName(1,unitsVariant)).toEqual '' + expect(Math.round).not.toHaveBeenCalled() + + it "calls Math.round() if variant_unit is 'weight' or 'volume'", -> + unitsVariant = { variant_unit: "weight" } + scope.formattedValueWithUnitName(1,unitsVariant) + expect(Math.round).toHaveBeenCalled() + scope.selectedUnitsVariant = { variant_unit: "volume" } + scope.formattedValueWithUnitName(1,unitsVariant) + expect(Math.round).toHaveBeenCalled() + + it "calls Math.round with the quotient of scale and value, multiplied by 1000", -> + unitsVariant = { variant_unit: "weight" } + spyOn(VariantUnitManager, "getScale").andReturn 5 + scope.formattedValueWithUnitName(10, unitsVariant) + expect(Math.round).toHaveBeenCalledWith 10/5 * 1000 + + it "returns the result of Math.round divided by 1000, followed by the result of getUnitName", -> + unitsVariant = { variant_unit: "weight" } + spyOn(VariantUnitManager, "getScale").andReturn 1000 + spyOn(VariantUnitManager, "getUnitName").andReturn "kg" + expect(scope.formattedValueWithUnitName(2000,unitsVariant)).toEqual "2 kg" + + describe "updating the price upon updating the weight of a line item", -> + beforeEach -> + LineItems.pristineByID = { 1: { price: 2.00, quantity: 1, final_weight_volume: 2000 } } + + it "updates the price if the weight is changed", -> + scope.filteredLineItems = [ + { id: 1, price: 2.00, quantity: 1, final_weight_volume: 4000 } + ] + scope.weightAdjustedPrice(scope.filteredLineItems[0]) + expect(scope.filteredLineItems[0].price).toEqual 4.00 + + it "doesn't update the price if the weight <= 0", -> + scope.filteredLineItems = [ + { id: 1, price: 2.00, quantity: 1, final_weight_volume: 0 } + ] + scope.weightAdjustedPrice(scope.filteredLineItems[0]) + expect(scope.filteredLineItems[0].price).toEqual 2.00 + + it "doesn't update the price if the weight is an empty string", -> + scope.filteredLineItems = [ + { id: 1, price: 2.00, quantity: 1, final_weight_volume: "" } + ] + scope.weightAdjustedPrice(scope.filteredLineItems[0]) + expect(scope.filteredLineItems[0].price).toEqual 2.00 + + describe "updating final_weight_volume upon updating the quantity for a line_item", -> + beforeEach -> + LineItems.pristineByID = { 1: { price: 2.00, quantity: 1, final_weight_volume: 2000 } } + spyOn(scope, "weightAdjustedPrice") + + it "updates the weight if the quantity is changed, then calls weightAdjustedPrice()", -> + scope.filteredLineItems = [ + { id: 1, price: 2.00, quantity: 2, final_weight_volume: 0 } + ] + scope.updateOnQuantity(scope.filteredLineItems[0]) + expect(scope.filteredLineItems[0].final_weight_volume).toEqual 4000 + expect(scope.weightAdjustedPrice).toHaveBeenCalled() + + it "doesn't update the weight if the quantity <= 0", -> + scope.filteredLineItems = [ + { id: 1, price: 2.00, quantity: 0, final_weight_volume: 1000 } + ] + scope.updateOnQuantity(scope.filteredLineItems[0]) + expect(scope.filteredLineItems[0].final_weight_volume).toEqual 1000 + + it "doesn't update the weight if the quantity is an empty string", -> + scope.filteredLineItems = [ + { id: 1, price: 2.00, quantity: "", final_weight_volume: 1000 } + ] + scope.updateOnQuantity(scope.filteredLineItems[0]) + expect(scope.filteredLineItems[0].final_weight_volume).toEqual 1000 + + +describe "Auxiliary functions", -> + describe "getting a zero filled two digit number", -> + it "returns the number as a string if its value is greater than or equal to 10", -> + expect(twoDigitNumber(10)).toEqual "10" + expect(twoDigitNumber(15)).toEqual "15" + expect(twoDigitNumber(99)).toEqual "99" + + it "returns the number formatted as a zero filled string if its value is less than 10", -> + expect(twoDigitNumber(0)).toEqual "00" + expect(twoDigitNumber(1)).toEqual "01" + expect(twoDigitNumber(9)).toEqual "09" + + describe "formatting dates and times", -> + date = null + + beforeEach -> + date = new Date + date.setYear(2010) + date.setMonth(4) # Zero indexed, so 4 is May + date.setDate(15) + date.setHours(5) + date.setMinutes(10) + date.setSeconds(30) + + it "returns a date formatted as yyyy-mm-dd", -> + expect(formatDate(date)).toEqual "2010-05-15" + + it "returns a time formatted as hh-MM:ss", -> + expect(formatTime(date)).toEqual "05:10:30"