Merge branch 'master' into 2-0-stable-jan-29th

This commit is contained in:
luisramos0
2019-01-29 12:17:59 +00:00
34 changed files with 663 additions and 209 deletions

View File

@@ -72,10 +72,11 @@ gem 'roadie-rails', '~> 1.1.1'
gem 'figaro'
gem 'blockenspiel'
gem 'acts-as-taggable-on', '~> 3.4'
gem 'paper_trail', '~> 3.0.8'
gem 'paper_trail', '~> 5.2.3'
gem 'diffy'
gem 'skylight', '< 2.0'
gem 'combine_pdf'
gem 'wicked_pdf'
gem 'wkhtmltopdf-binary'

View File

@@ -59,7 +59,7 @@ GIT
json (>= 1.7.7)
kaminari (~> 0.14.1)
money (= 5.1.1)
paperclip (~> 3.4.1)
paperclip (~> 3.0)
paranoia (~> 1.3)
rails (~> 3.2.14)
ransack (= 0.7.2)
@@ -222,6 +222,8 @@ GEM
execjs
coffee-script-source (1.10.0)
colorize (0.8.1)
combine_pdf (1.0.15)
ruby-rc4 (>= 0.1.5)
compass (1.0.3)
chunky_png (~> 1.2)
compass-core (~> 1.0.2)
@@ -242,7 +244,7 @@ GEM
safe_yaml (~> 1.0.0)
css_parser (1.6.0)
addressable
daemons (1.2.6)
daemons (1.3.1)
dalli (2.7.2)
database_cleaner (0.7.1)
db2fog (0.9.0)
@@ -256,8 +258,8 @@ GEM
rails (>= 3.1)
delayed_job (4.1.5)
activesupport (>= 3.0, < 5.3)
delayed_job_active_record (4.1.2)
activerecord (>= 3.0, < 5.2)
delayed_job_active_record (4.1.3)
activerecord (>= 3.0, < 5.3)
delayed_job (>= 3.0, < 5)
devise (2.2.8)
bcrypt-ruby (~> 3.0)
@@ -267,7 +269,7 @@ GEM
devise-encryptable (0.1.2)
devise (>= 2.1.0)
diff-lcs (1.3)
diffy (3.1.0)
diffy (3.3.0)
docile (1.3.1)
dry-inflector (0.1.2)
em-websocket (0.5.1)
@@ -442,9 +444,9 @@ GEM
foundation-icons-sass-rails (3.0.0)
railties (>= 3.1.1)
sass-rails (>= 3.1.1)
foundation-rails (5.5.0.0)
foundation-rails (5.5.2.1)
railties (>= 3.1.0)
sass (>= 3.2.0, < 3.4)
sass (>= 3.3.0, < 3.5)
fuubar (2.3.2)
rspec-core (~> 3.0)
ruby-progressbar (~> 1.4)
@@ -541,11 +543,11 @@ GEM
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
oj (3.7.4)
oj (3.7.8)
orm_adapter (0.5.0)
paper_trail (3.0.9)
activerecord (>= 3.0, < 5.0)
activesupport (>= 3.0, < 5.0)
paper_trail (5.2.3)
activerecord (>= 3.0, < 6.0)
request_store (~> 1.1)
paperclip (3.4.2)
activemodel (>= 3.0.0)
activerecord (>= 3.0.0)
@@ -623,6 +625,8 @@ GEM
json (~> 1.4)
redcarpet (3.2.3)
ref (2.0.0)
request_store (1.4.1)
rack (>= 1.4)
roadie (3.4.0)
css_parser (~> 1.4)
nokogiri (~> 1.5)
@@ -669,6 +673,7 @@ GEM
unicode-display_width (~> 1.0, >= 1.0.1)
ruby-ole (1.2.12.1)
ruby-progressbar (1.10.0)
ruby-rc4 (0.1.5)
rubyzip (1.2.2)
safe_yaml (1.0.4)
sass (3.3.14)
@@ -771,6 +776,7 @@ DEPENDENCIES
capybara (>= 2.15.4)
chromedriver-helper
coffee-rails (~> 3.2.1)
combine_pdf
compass-rails
custom_error_message!
daemons
@@ -811,7 +817,7 @@ DEPENDENCIES
oauth2 (~> 1.4.1)
ofn-qz!
oj
paper_trail (~> 3.0.8)
paper_trail (~> 5.2.3)
paperclip (~> 3.4.1)
pg
pry-byebug (>= 3.4.3)

View File

@@ -0,0 +1,32 @@
angular.module("admin.orders").controller "bulkInvoiceCtrl", ($scope, $http, $timeout) ->
$scope.createBulkInvoice = ->
$scope.invoice_id = null
$scope.poll = 1
$scope.loading = true
$scope.message = null
$scope.error = null
$scope.poll_wait = 5 # 5 Seconds between each check
$scope.poll_retries = 80 # Maximum checks before stopping
$http.post('/admin/orders/invoices', {order_ids: $scope.selected_orders}).success (data) ->
$scope.invoice_id = data
$scope.pollBulkInvoice()
$scope.pollBulkInvoice = ->
$timeout($scope.nextPoll, $scope.poll_wait * 1000)
$scope.nextPoll = ->
$http.get('/admin/orders/invoices/'+$scope.invoice_id+'/poll').success (data) ->
$scope.loading = false
$scope.message = t('js.admin.orders.index.bulk_invoice_created')
.error (data) ->
$scope.poll++
if $scope.poll > $scope.poll_retries
$scope.loading = false
$scope.error = t('js.admin.orders.index.bulk_invoice_failed')
return
$scope.pollBulkInvoice()

View File

@@ -1,4 +1,4 @@
angular.module("admin.orders").controller "ordersCtrl", ($scope, RequestMonitor, Orders, SortOptions) ->
angular.module("admin.orders").controller "ordersCtrl", ($scope, RequestMonitor, Orders, SortOptions, $window, $filter) ->
$scope.RequestMonitor = RequestMonitor
$scope.pagination = Orders.pagination
$scope.orders = Orders.all
@@ -8,6 +8,11 @@ angular.module("admin.orders").controller "ordersCtrl", ($scope, RequestMonitor,
{id: 50, name: t('js.admin.orders.index.per_page', results: 50)},
{id: 100, name: t('js.admin.orders.index.per_page', results: 100)}
]
$scope.selected_orders = []
$scope.checkboxes = {}
$scope.selected = false
$scope.select_all = false
$scope.poll = 0
$scope.initialise = ->
$scope.per_page = 15
@@ -17,6 +22,7 @@ angular.module("admin.orders").controller "ordersCtrl", ($scope, RequestMonitor,
$scope.fetchResults()
$scope.fetchResults = (page=1) ->
$scope.resetSelected()
Orders.index({
'q[completed_at_lt]': $scope['q']['completed_at_lt'],
'q[completed_at_gt]': $scope['q']['completed_at_gt'],
@@ -35,6 +41,26 @@ angular.module("admin.orders").controller "ordersCtrl", ($scope, RequestMonitor,
page: page
})
$scope.resetSelected = ->
$scope.selected_orders.length = 0
$scope.selected = false
$scope.select_all = false
$scope.checkboxes = {}
$scope.toggleSelection = (id) ->
index = $scope.selected_orders.indexOf(id)
if index == -1
$scope.selected_orders.push(id)
else
$scope.selected_orders.splice(index, 1)
$scope.toggleAll = ->
$scope.selected_orders.length = 0
$scope.orders.forEach (order) ->
$scope.checkboxes[order.id] = $scope.select_all
$scope.selected_orders.push order.id if $scope.select_all
$scope.$watch 'sortOptions', (sort) ->
if sort && sort.predicate != ""
$scope.sorting = sort.predicate + ' desc' if sort.reverse

View File

@@ -0,0 +1,5 @@
angular.module("admin.orders").directive "invoicesModal", ($modal) ->
restrict: 'C'
link: (scope, elem, attrs, ctrl) ->
elem.on "click", (ev) =>
scope.uploadModal = $modal.open(templateUrl: 'admin/modals/bulk_invoice.html', controller: ctrl, scope: scope, windowClass: 'simple-modal')

View File

@@ -1 +1 @@
angular.module("admin.orders", ['admin.indexUtils', 'ngResource'])
angular.module("admin.orders", ['admin.indexUtils', 'ngResource', 'mm.foundation'])

View File

@@ -2,5 +2,5 @@ angular.module("ofn.admin").directive "imageModal", ($modal, ProductImageService
restrict: 'C'
link: (scope, elem, attrs, ctrl) ->
elem.on "click", (ev) =>
scope.uploadModal = $modal.open(templateUrl: 'admin/modals/image_upload.html', controller: ctrl, scope: scope, windowClass: 'product-image-upload')
scope.uploadModal = $modal.open(templateUrl: 'admin/modals/image_upload.html', controller: ctrl, scope: scope, windowClass: 'simple-modal')
ProductImageService.configure(scope.product)

View File

@@ -0,0 +1,15 @@
%h4.modal-title
= t('js.admin.orders.index.compiling_invoices')
%p.message{ ng: { show: 'message' } }
{{message}}
%p.error{ ng: { show: 'error' } }
{{error}}
%img.spinner{ src: "/assets/spinning-circles.svg", ng: { show: "loading" } }
%p{ ng: { show: "loading" } }
= t('js.admin.orders.index.please_wait')
%a.button{ target: '_blank', ng: { click: 'showBulkInvoice()', href: '/admin/orders/invoices/{{invoice_id}}', show: "!loading && !error" } }
= t('js.admin.orders.index.view_file')

View File

@@ -17,4 +17,3 @@
@import 'variables';
@import 'components/*';
@import '*';
@import 'pages/*';

View File

@@ -1,6 +1,6 @@
@import '../variables';
.reveal-modal.product-image-upload {
.reveal-modal.simple-modal {
width: 300px;
.close-reveal-modal {

View File

@@ -103,3 +103,34 @@ table.index td.actions {
font-size: 1.2em;
}
}
.index-controls {
button {
float: right;
&:disabled {
background-color: $disabled-button;
}
}
}
.simple-modal {
text-align: center;
.modal-title {
margin-bottom: 1.5em;
}
.message, .error {
margin-bottom: 1em;
}
.spinner {
margin-bottom: 1em;
}
.error {
color: $warning-red;
}
}

View File

@@ -1,86 +0,0 @@
@import typography
.darkswarm
navigation
display: block
background: #f7f7f7
distributor.details
box-sizing: border-box
display: block
min-height: 150px
padding: 30px 0px 20px 0px
select
width: 200px
position: relative
img
display: block
height: 100px
width: 100px
margin-right: 12px
location
@include headingFont
@media all and (max-width: 768px)
location, location + small
display: block
#distributor_title h3
margin-top: 0
@media all and (max-width: 768px)
margin-bottom: 8px
ordercycle
text-align: right
p
max-width: 400px
h4 i
margin-right: 0.3rem
@media all and (max-width: 640px)
float: left
clear: left
text-align: left
padding: 12px 10px
width: 100%
margin-top: 10px
background: #e5e5e5
p
max-width: 100%
float: right
form.custom
text-align: right
& > strong
line-height: 2.5
font-size: 1.29em
padding-right: 14px
@media all and (max-width: 768px)
select
width: inherit
display: inline-block
border-width: 1px
border-color: #999
color: #666
font-size: 1em
margin-bottom: 0
padding: 8px 20px 8px 12px
@media all and (max-width: 768px)
font-size: 0.875em
@media screen and (-webkit-min-device-pixel-ratio:0)
font-size: 16px
closing
@include headingFont
@media all and (max-width: 768px)
font-size: 1.2em
padding-bottom: 10px
color: black
font-size: 1.5em
display: block
padding-bottom: 12px
span
@media all and (max-width: 768px)
font-size: 0.875em

View File

@@ -0,0 +1,103 @@
@import "typography";
.darkswarm navigation {
display: block;
background: #f7f7f7;
distributor.details {
box-sizing: border-box;
display: block;
min-height: 150px;
padding: 30px 0 20px 0;
position: relative;
select {
width: 200px;
}
img {
display: block;
height: 100px;
width: 100px;
margin-right: 12px;
}
location {
@include headingFont;
}
@media all and (max-width: 768px) {
location, location + small {
display: block;
}
}
#distributor_title h3 {
margin-top: 0;
@media all and (max-width: 768px) {
margin-bottom: 8px;
}
}
ordercycle {
text-align: right;
float: right;
p {
max-width: 400px;
}
h4 i {
margin-right: 0.3rem;
}
@media all and (max-width: 640px) {
float: left;
clear: left;
text-align: left;
padding: 12px 10px;
width: 100%;
margin-top: 10px;
background: #e5e5e5;
p {
max-width: 100%;
}
}
form.custom {
text-align: right;
& > strong {
line-height: 2.5;
font-size: 1.29em;
padding-right: 14px;
}
select {
width: inherit;
display: inline-block;
border: 1px #999;
color: #666;
font-size: 1em;
margin-bottom: 0;
padding: 8px 20px 8px 12px;
@media all and (max-width: 768px) {
font-size: 0.875em;
}
@media screen and (-webkit-min-device-pixel-ratio: 0) {
font-size: 16px;
}
}
}
closing {
@include headingFont;
color: black;
font-size: 1.5em;
display: block;
padding-bottom: 12px;
@media all and (max-width: 768px) {
font-size: 1.2em;
padding-bottom: 10px;
}
span {
@media all and (max-width: 768px) {
font-size: 0.875em;
}
}
}
}
}
}

View File

@@ -1,86 +0,0 @@
@import mixins
@import branding
@import animations
.order-summary
background-color: #e1f0f5
padding: 1em
width: 100%
border: none
color: inherit
checkout
display: block
@media all and (max-width: 640px)
&.row .row
margin-left: 0
margin-right: 0
orderdetails
.button, table
width: 100%
@media all and (max-width: 640px)
form.edit_order
border: 1px solid $disabled-bright
margin-bottom: 2rem
#details, #billing, #shipping, #payment
border: 0
margin: 1em 0
padding: 0
.content
border: 1px solid #efefef
h5
margin: 0
padding: 0.65em
background: #f7f7f7
.label
font-size: 1em
padding: 0.3rem 0.35rem 0.275rem
// Logic to turn on & off the alerts for success against each fieldset
label, label.alert, label.success, &.valid label.alert, &.dirty label.success
display: none
&.dirty label.alert
display: inline
&.dirty.valid label.alert
display: none
&.valid label.success
display: inline
h5.dirty
background: #f7ccc5
h5.valid, h5.dirty.valid
background: #bfefd1
orderdetails table tr th
text-align: left
// Logic to swap out up / down accordion icons
//Foundation overrides
dd > a
@include csstrans
background: $disabled-light !important
dd > a:hover
background: $disabled-v-dark !important
color: white
dd
span.accordion-up
display: none
span.accordion-down
display: inline
&.open
span.accordion-up
display: inline
span.accordion-down
display: none
.error
color: #c82020

View File

@@ -0,0 +1,117 @@
@import "mixins";
@import "branding";
@import "animations";
.order-summary {
background-color: #e1f0f5;
padding: 1em;
width: 100%;
border: none;
color: inherit;
}
checkout {
display: block;
@media all and (max-width: 640px) {
&.row .row {
margin-left: 0;
margin-right: 0;
}
}
orderdetails {
.button, table {
width: 100%;
}
@media all and (max-width: 640px) {
form.edit_order {
border: 1px solid $disabled-bright;
margin-bottom: 2rem;
}
}
}
#details, #billing, #shipping, #payment {
border: 0;
margin: 1em 0;
padding: 0;
.content {
border: 1px solid #efefef;
}
}
h5 {
margin: 0;
padding: 0.65em;
background: #f7f7f7;
.label {
font-size: 1em;
padding: 0.3rem 0.35rem 0.275rem;
}
// Logic to turn on & off the alerts for success against each fieldset
label, label.alert, label.success, &.valid label.alert, &.dirty label.success {
display: none;
}
&.dirty label.alert {
display: inline;
}
&.dirty.valid label.alert {
display: none;
}
&.valid label.success {
display: inline;
}
}
h5.dirty {
background: #f7ccc5;
}
h5.valid, h5.dirty.valid {
background: #bfefd1;
}
orderdetails table tr th {
text-align: left;
}
// Logic to swap out up / down accordion icons
//Foundation overrides
dd > a {
@include csstrans;
background: $disabled-light !important;
}
dd > a:hover {
background: $disabled-v-dark !important;
color: white;
}
dd {
span.accordion-up {
display: none;
}
span.accordion-down {
display: inline;
}
&.open {
span.accordion-up {
display: inline;
}
span.accordion-down {
display: none;
}
}
}
.error {
color: #c82020;
}
}

View File

@@ -4,3 +4,11 @@
fieldset {
border: 0;
}
.user-form {
margin-left: auto;
margin-right: auto;
max-width: 1184px;
padding-left: .9375rem;
padding-right: .9375rem;
}

View File

@@ -0,0 +1,31 @@
module Spree
module Admin
class InvoicesController < Spree::Admin::BaseController
respond_to :json
def create
invoice_service = BulkInvoiceService.new
invoice_service.start_pdf_job(params[:order_ids])
render json: invoice_service.id, status: :ok
end
def show
invoice_id = params[:id]
invoice_pdf = BulkInvoiceService.new.filepath(invoice_id)
send_file(invoice_pdf, type: 'application/pdf', disposition: :inline)
end
def poll
invoice_id = params[:invoice_id]
if BulkInvoiceService.new.invoice_created? invoice_id
render json: { created: true }, status: :ok
else
render json: { created: false }, status: :unprocessable_entity
end
end
end
end
end

View File

@@ -37,8 +37,10 @@ Spree::Admin::OrdersController.class_eval do
end
def invoice
template = if Spree::Config.invoice_style2? then "spree/admin/orders/invoice2" else "spree/admin/orders/invoice" end
pdf = render_to_string pdf: "invoice-#{@order.number}.pdf", template: template, formats: [:html], encoding: "UTF-8"
pdf = render_to_string pdf: "invoice-#{@order.number}.pdf",
template: invoice_template,
formats: [:html], encoding: "UTF-8"
Spree::OrderMailer.invoice_email(@order.id, pdf).deliver
flash[:success] = t('admin.orders.invoice_email_sent')
@@ -46,8 +48,7 @@ Spree::Admin::OrdersController.class_eval do
end
def print
template = if Spree::Config.invoice_style2? then "spree/admin/orders/invoice2" else "spree/admin/orders/invoice" end
render pdf: "invoice-#{@order.number}", template: template, encoding: "UTF-8"
render pdf: "invoice-#{@order.number}", template: invoice_template, encoding: "UTF-8"
end
def print_ticket
@@ -60,6 +61,10 @@ Spree::Admin::OrdersController.class_eval do
private
def invoice_template
Spree::Config.invoice_style2? ? "spree/admin/orders/invoice2" : "spree/admin/orders/invoice"
end
def require_distributor_abn
unless @order.distributor.abn.present?
flash[:error] = t(:must_have_valid_business_number, enterprise_name: @order.distributor.name)

View File

@@ -208,7 +208,9 @@ class AbilityDecorator
# during the order creation process from the admin backend
order.distributor.nil? || user.enterprises.include?(order.distributor) || order.order_cycle.andand.coordinated_by?(user)
end
can [:admin, :bulk_management, :managed], Spree::Order if user.admin? || user.enterprises.any?(&:is_distributor)
can [:admin, :bulk_management, :managed, :bulk_invoice], Spree::Order do
user.admin? || user.enterprises.any?(&:is_distributor)
end
can [:admin, :visible], Enterprise
can [:admin, :index, :create, :update, :destroy], :line_item
can [:admin, :index, :create], Spree::LineItem

View File

@@ -0,0 +1,56 @@
class BulkInvoiceService
include WickedPdf::PdfHelper
attr_reader :id
def initialize
@id = new_invoice_id
end
def start_pdf_job(order_ids)
pdf = CombinePDF.new
orders = Spree::Order.where(id: order_ids)
orders.each do |order|
invoice = renderer.render_to_string pdf: "invoice-#{order.number}.pdf",
template: invoice_template,
formats: [:html], encoding: "UTF-8",
locals: { :@order => order }
pdf << CombinePDF.parse(invoice)
end
pdf.save "#{file_directory}/#{@id}.pdf"
end
handle_asynchronously :start_pdf_job
def invoice_created?(invoice_id)
File.exist? filepath(invoice_id)
end
def filepath(invoice_id)
"#{directory}/#{invoice_id}.pdf"
end
private
def new_invoice_id
Time.zone.now.to_i.to_s
end
def directory
'tmp/invoices'
end
def renderer
ApplicationController.new
end
def invoice_template
Spree::Config.invoice_style2? ? "spree/admin/orders/invoice2" : "spree/admin/orders/invoice"
end
def file_directory
Dir.mkdir(directory) unless File.exist?(directory)
directory
end
end

View File

@@ -2,4 +2,4 @@
I18n.default_locale = "#{I18n.default_locale}";
I18n.locale = "#{I18n.locale}";
I18n.fallbacks = true;
moment.lang([I18n.locale, 'en']);
moment.locale([I18n.locale, 'en']);

View File

@@ -16,14 +16,19 @@
- content_for :table_filter do
= render partial: 'filters'
.row
.row.index-controls{'ng-show' => '!RequestMonitor.loading && orders.length > 0'}
= render partial: 'per_page_controls'
%button.invoices-modal{'ng-controller' => 'bulkInvoiceCtrl', 'ng-click' => 'createBulkInvoice()', 'ng-disabled' => 'selected_orders.length == 0'}
= t('.print_invoices')
%table#listing_orders.index.responsive{width: "100%", 'ng-init' => 'initialise()', 'ng-show' => "!RequestMonitor.loading && orders.length > 0" }
%colgroup
%col{style: "width: 10%"}
%col{style: "width: 3%"}
%thead
%tr
%th
%input{type: 'checkbox', 'ng-click' => 'toggleAll()', 'ng-model' => 'select_all'}
%th
= t(:products_distributor)
%th
@@ -39,6 +44,8 @@
%th.actions
%tbody
%tr{ng: {repeat: 'order in orders track by $index', class: {even: "'even'", odd: "'odd'"}}, 'ng-class' => "'state-{{order.state}}'"}
%td.align-center
%input{type: 'checkbox', 'ng-model' => 'checkboxes[order.id]', 'ng-change' => 'toggleSelection(order.id)'}
%td.align-center
{{order.distributor_name}}
%td.align-center

View File

@@ -30,13 +30,14 @@
&nbsp;
%td{ :align => "right" }
%strong= "#{t('.to')}: #{@order.ship_address.full_name}"
- if @order.customer.code.present?
- if @order.andand.customer.andand.code.present?
%br
= "#{t('.code')}: #{@order.customer.code}"
%br
= @order.ship_address.full_address
%br
= "#{@order.customer.email},"
- if @order.andand.customer.andand.email.present?
= "#{@order.customer.email},"
= "#{@order.bill_address.phone}"
= render 'spree/admin/orders/invoice_table'

View File

@@ -45,7 +45,7 @@
= t :invoice_billing_address
%br
%strong= @order.ship_address.full_name
- if @order.customer.code.present?
- if @order.andand.customer.andand.code.present?
%br
= "Code: #{@order.customer.code}"
%br

View File

@@ -50,6 +50,6 @@
- if @order.order_cycle.andand.pickup_instructions_for(@order.distributor).present?
%p
%strong
= t :email_shipping_collection_time
= t :email_shipping_collection_instructions
%br
#{@order.order_cycle.pickup_instructions_for(@order.distributor)}

View File

@@ -1,3 +1,3 @@
.darkswarm
.row
.user-form
= render 'form'

View File

@@ -2601,6 +2601,11 @@ See the %{link} to find out more about %{sitename}'s features and to start using
orders:
index:
per_page: "%{results} per page"
view_file: View File
compiling_invoices: Compiling Invoices
bulk_invoice_created: Bulk Invoice created
bulk_invoice_failed: Failed to create Bulk Invoice
please_wait: Please wait until the PDF is ready before closing this modal.
resend_user_email_confirmation:
resend: "Resend"
sending: "Resend..."
@@ -2733,6 +2738,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using
no_orders_found: "No Orders Found"
results_found: "%{number} Results found."
viewing: "Viewing %{start} to %{end}."
print_invoices: "Print Invoices"
invoice:
issued_on: Issued on
tax_invoice: TAX INVOICE

View File

@@ -74,6 +74,12 @@ Spree::Core::Engine.routes.prepend do
get :print, on: :member
get :print_ticket, on: :member
get :managed, on: :collection
collection do
resources :invoices, only: [:create, :show] do
get :poll
end
end
end
end

View File

@@ -0,0 +1,43 @@
require 'spec_helper'
describe Spree::Admin::InvoicesController, type: :controller do
let(:order) { create(:order_with_totals_and_distribution) }
let(:user) { create(:admin_user) }
before do
allow(controller).to receive(:spree_current_user) { user }
end
describe "#create" do
it "enqueues a job to create a bulk invoice and returns the filename" do
expect do
spree_post :create, order_ids: [order.id]
end.to enqueue_job Delayed::PerformableMethod
expect(Delayed::Job.last.payload_object.method_name).to eq :start_pdf_job_without_delay
end
end
describe "#poll" do
let(:invoice_id) { '479186263' }
context "when the file is available" do
it "returns true" do
allow(File).to receive(:exist?).and_return(true)
spree_get :poll, invoice_id: invoice_id
expect(response.body).to eq({ created: true }.to_json)
expect(response.status).to eq 200
end
end
context "when the file is not available" do
it "returns false" do
spree_get :poll, invoice_id: invoice_id
expect(response.body).to eq({ created: false }.to_json)
expect(response.status).to eq 422
end
end
end
end

View File

@@ -765,9 +765,9 @@ feature %q{
end
# Shows upload modal
expect(page).to have_selector "div.reveal-modal.product-image-upload"
expect(page).to have_selector "div.reveal-modal"
within "div.reveal-modal.product-image-upload" do
within "div.reveal-modal" do
# Shows preview of current image
expect(page).to have_css "img.preview"
@@ -779,7 +779,7 @@ feature %q{
expect(page).to have_no_css "img.spinner", visible: true
end
expect(page).to have_no_selector "div.reveal-modal.product-image-upload"
expect(page).to have_no_selector "div.reveal-modal"
within "table#listing_products tr#p_#{product.id}" do
# New thumbnail is shown in image column
@@ -789,7 +789,7 @@ feature %q{
page.find("a.image-modal").click
end
expect(page).to have_selector "div.reveal-modal.product-image-upload"
expect(page).to have_selector "div.reveal-modal"
end
end
end

View File

@@ -115,13 +115,15 @@ feature "Using embedded shopfront functionality", js: true do
end
def login_with_modal
expect(page).to have_selector 'div.login-modal', visible: true
page.has_selector? 'div.login-modal', visible: true
within 'div.login-modal' do
fill_in "Email", with: user.email
fill_in "Password", with: user.password
find('input[type="submit"]').click
end
page.has_no_selector? 'div.login-modal', visible: true
end
def logout_via_navigation

View File

@@ -197,6 +197,6 @@ xdescribe ProxyOrder, type: :model do
# We still need to use be_within, because the Database timestamp is not as
# accurate as the Rails timestamp. If we use `eq`, we have differing nano
# seconds.
expect(subject.reload.canceled_at).to be_within(1.second).of Time.zone.now
expect(subject.reload.canceled_at).to be_within(2.second).of Time.zone.now
end
end

View File

@@ -514,5 +514,89 @@ module Spree
end
end
end
describe "permissions for variant overrides" do
let!(:distributor) { create(:distributor_enterprise) }
let!(:producer) { create(:supplier_enterprise) }
let!(:product) { create(:product, supplier: producer) }
let!(:variant) { create(:variant, product: product) }
let!(:variant_override) { create(:variant_override, hub: distributor, variant: variant) }
subject { user }
let(:manage_actions) { [:admin, :index, :read, :update, :bulk_update, :bulk_reset] }
describe "when admin" do
let(:user) { create(:admin_user) }
it "should have permission" do
is_expected.to have_ability(manage_actions, for: variant_override)
end
end
describe "when user of the producer" do
let(:user) { producer.owner }
it "should not have permission" do
is_expected.not_to have_ability(manage_actions, for: variant_override)
end
end
describe "when user of the distributor" do
let(:user) { distributor.owner }
it "should not have permission" do
is_expected.not_to have_ability(manage_actions, for: variant_override)
end
end
describe "when user of the distributor which is also the producer" do
let(:user) { distributor.owner }
let!(:distributor) { create(:distributor_enterprise, is_primary_producer: true, sells: "any") }
let!(:producer) { distributor }
it "should have permission" do
is_expected.to have_ability(manage_actions, for: variant_override)
end
end
describe "when owner of the distributor with add_to_order_cycle permission to the producer" do
let!(:unauthorized_enterprise) do
create(:enterprise, sells: "any").tap do |record|
create(:enterprise_relationship, parent: producer, child: record, permissions_list: [:add_to_order_cycle])
end
end
let(:user) { unauthorized_enterprise.owner }
it "should not have permission" do
is_expected.not_to have_ability(manage_actions, for: variant_override)
end
end
describe "when owner of the enterprise with create_variant_overrides permission to the producer" do
let!(:authorized_enterprise) do
create(:enterprise, sells: "any").tap do |record|
create(:enterprise_relationship, parent: producer, child: record, permissions_list: [:create_variant_overrides])
end
end
let(:user) { authorized_enterprise.owner }
it "should not have permission" do
is_expected.not_to have_ability(manage_actions, for: variant_override)
end
describe "when the enterprise is not a distributor" do
let!(:authorized_enterprise) do
create(:enterprise, sells: "none").tap do |record|
create(:enterprise_relationship, parent: producer, child: record, permissions_list: [:create_variant_overrides])
end
end
it "should not have permission" do
is_expected.not_to have_ability(manage_actions, for: variant_override)
end
end
end
end
end
end

View File

@@ -0,0 +1,40 @@
require 'spec_helper'
describe BulkInvoiceService do
let(:service) { BulkInvoiceService.new }
describe "#start_pdf_job" do
it "starts a background process to create a pdf with multiple invoices" do
expect do
service.start_pdf_job [1, 2]
end.to enqueue_job Delayed::PerformableMethod
expect(Delayed::Job.last.payload_object.method_name).to eq :start_pdf_job_without_delay
end
end
describe "#invoice_created?" do
context "when the invoice has been created" do
it "returns true" do
allow(File).to receive(:exist?).and_return(true)
created = service.invoice_created? '45891723'
expect(created).to be_truthy
end
end
context "when the invoice has not been created" do
it "returns false" do
created = service.invoice_created? '1234567'
expect(created).to_not be_truthy
end
end
end
describe "#filepath" do
it "returns the filepath of a given invoice" do
filepath = service.filepath '1234567'
expect(filepath).to eq 'tmp/invoices/1234567.pdf'
end
end
end