mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-03-31 06:31:41 +00:00
Merge pull request #10523 from rioug/10431-voucher-minimum-backend
Voucher bare minimum backoffice
This commit is contained in:
36
app/controllers/admin/vouchers_controller.rb
Normal file
36
app/controllers/admin/vouchers_controller.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class VouchersController < ResourceController
|
||||
before_action :load_enterprise
|
||||
|
||||
def new
|
||||
@voucher = Voucher.new
|
||||
end
|
||||
|
||||
def create
|
||||
voucher_params = permitted_resource_params.merge(enterprise: @enterprise)
|
||||
@voucher = Voucher.create(voucher_params)
|
||||
|
||||
if @voucher.save
|
||||
redirect_to(
|
||||
"#{edit_admin_enterprise_path(@enterprise)}#vouchers_panel",
|
||||
flash: { success: flash_message_for(@voucher, :successfully_created) }
|
||||
)
|
||||
else
|
||||
flash[:error] = @voucher.errors.full_messages.to_sentence
|
||||
render :new
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def load_enterprise
|
||||
@enterprise = Enterprise.find_by permalink: params[:enterprise_id]
|
||||
end
|
||||
|
||||
def permitted_resource_params
|
||||
params.require(:voucher).permit(:code)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -14,6 +14,7 @@ module Admin
|
||||
producers.size == 1 ? producers.first.id : nil
|
||||
end
|
||||
|
||||
# rubocop:disable Metrics/MethodLength
|
||||
def enterprise_side_menu_items(enterprise)
|
||||
is_shop = enterprise.sells != "none"
|
||||
show_properties = !!enterprise.is_primary_producer
|
||||
@@ -34,6 +35,7 @@ module Admin
|
||||
{ name: 'shipping_methods', icon_class: "icon-truck", show: show_shipping_methods },
|
||||
{ name: 'payment_methods', icon_class: "icon-money", show: show_payment_methods },
|
||||
{ name: 'enterprise_fees', icon_class: "icon-tasks", show: show_enterprise_fees },
|
||||
{ name: 'vouchers', icon_class: "icon-ticket", show: true },
|
||||
{ name: 'enterprise_permissions', icon_class: "icon-plug", show: true,
|
||||
href: admin_enterprise_relationships_path },
|
||||
{ name: 'inventory_settings', icon_class: "icon-list-ol", show: is_shop },
|
||||
@@ -42,5 +44,6 @@ module Admin
|
||||
{ name: 'users', icon_class: "icon-user", show: true }
|
||||
]
|
||||
end
|
||||
# rubocop:enable Metrics/MethodLength
|
||||
end
|
||||
end
|
||||
|
||||
@@ -65,6 +65,7 @@ class Enterprise < ApplicationRecord
|
||||
has_many :inventory_items
|
||||
has_many :tag_rules
|
||||
has_one :stripe_account, dependent: :destroy
|
||||
has_many :vouchers
|
||||
|
||||
delegate :latitude, :longitude, :city, :state_name, to: :address
|
||||
|
||||
|
||||
@@ -179,6 +179,8 @@ module Spree
|
||||
can [:admin, :create], :manager_invitation
|
||||
|
||||
can [:admin, :index], :oidc_setting
|
||||
|
||||
can [:admin, :create], Voucher
|
||||
end
|
||||
|
||||
def add_product_management_abilities(user)
|
||||
|
||||
15
app/models/voucher.rb
Normal file
15
app/models/voucher.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
# frozen_string_literal: false
|
||||
|
||||
class Voucher < ApplicationRecord
|
||||
belongs_to :enterprise
|
||||
|
||||
validates :code, presence: true, uniqueness: { scope: :enterprise_id }
|
||||
|
||||
def value
|
||||
10
|
||||
end
|
||||
|
||||
def display_value
|
||||
Spree::Money.new(value)
|
||||
end
|
||||
end
|
||||
@@ -9,6 +9,12 @@
|
||||
%fieldset.alpha.no-border-bottom{ id: "#{item[:name]}_panel", data: { "tabs-and-panels-target": "panel" }}
|
||||
%legend= t(".#{ item[:name] }.legend")
|
||||
|
||||
- when 'vouchers'
|
||||
- if feature?(:vouchers, spree_current_user)
|
||||
%fieldset.alpha.no-border-bottom{ id: "#{item[:name]}_panel", data: { "tabs-and-panels-target": "panel" }}
|
||||
%legend= t(".#{ item[:form_name] || item[:name] }.legend")
|
||||
= render "admin/enterprises/form/#{ item[:form_name] || item[:name] }", f: f
|
||||
|
||||
- else
|
||||
%fieldset.alpha.no-border-bottom{ id: "#{item[:name]}_panel", data: { "tabs-and-panels-target": "panel" }}
|
||||
%legend= t(".#{ item[:form_name] || item[:name] }.legend")
|
||||
|
||||
33
app/views/admin/enterprises/form/_vouchers.html.haml
Normal file
33
app/views/admin/enterprises/form/_vouchers.html.haml
Normal file
@@ -0,0 +1,33 @@
|
||||
.text-right
|
||||
%a.button{ href: "#{new_admin_enterprise_voucher_path(@enterprise)}"}
|
||||
= t('.add_new')
|
||||
%br
|
||||
|
||||
- if @enterprise.vouchers.present?
|
||||
%table
|
||||
%thead
|
||||
%tr
|
||||
%th= t('.voucher_code')
|
||||
%th= t('.rate')
|
||||
/%th= t('.label')
|
||||
/%th= t('.purpose')
|
||||
/%th= t('.expiry')
|
||||
/%th= t('.use_limit')
|
||||
/%th= t('.customers')
|
||||
/%th= t('.net_value')
|
||||
%tbody
|
||||
- @enterprise.vouchers.each do |voucher|
|
||||
%tr
|
||||
%td= voucher.code
|
||||
%td= voucher.display_value
|
||||
/%td
|
||||
/%td
|
||||
/%td
|
||||
/%td
|
||||
/%td
|
||||
/%td
|
||||
|
||||
- else
|
||||
%p.text-center
|
||||
= t('.no_voucher_yet')
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.side_menu#side_menu
|
||||
- if @enterprise
|
||||
- enterprise_side_menu_items(@enterprise).each do |item|
|
||||
- next unless item[:show]
|
||||
- next if !item[:show] || (item[:name] == 'vouchers' && !feature?(:vouchers, spree_current_user))
|
||||
%a.menu_item{ href: item[:href] || "##{item[:name]}_panel", id: item[:name], data: { action: "tabs-and-panels#changeActivePanel tabs-and-panels#changeActiveTab", "tabs-and-panels-target": "tab" }, class: item[:selected] }
|
||||
%i{ class: item[:icon_class] }
|
||||
%span= t(".enterprise.#{item[:name] }")
|
||||
|
||||
23
app/views/admin/vouchers/new.html.haml
Normal file
23
app/views/admin/vouchers/new.html.haml
Normal file
@@ -0,0 +1,23 @@
|
||||
= form_with model: @voucher, url: admin_enterprise_vouchers_path(@enterprise), html: { name: "voucher_form" } do |f|
|
||||
.row
|
||||
.sixteen.columns.alpha
|
||||
.four.columns.alpha.text-right
|
||||
%a.button{ href: "#{edit_admin_enterprise_path(@enterprise)}#!#vouchers_panel"}
|
||||
= t('.back')
|
||||
.twelve.columns.omega
|
||||
.row
|
||||
.eight.columns.text-center
|
||||
%legend= t(".legend")
|
||||
.four.columns.text-right
|
||||
= f.submit t('.save'), class: 'red'
|
||||
.row
|
||||
.alpha.four.columns
|
||||
= f.label :code, t('.voucher_code')
|
||||
.omega.eight.columns
|
||||
= f.text_area :code, rows: 6, class: 'fullwidth'
|
||||
.row
|
||||
.alpha.four.columns
|
||||
= f.label :amount, t('.voucher_amount')
|
||||
.omega.eight.columns
|
||||
= Spree::Money.currency_symbol
|
||||
= f.text_field :amount, value: @voucher.value, disabled: true
|
||||
@@ -4,7 +4,7 @@
|
||||
= tab :orders, :subscriptions, :customer_details, :adjustments, :payments, :return_authorizations, url: admin_orders_path('q[s]' => 'completed_at desc'), icon: 'icon-shopping-cart'
|
||||
= tab :reports, url: main_app.admin_reports_path, icon: 'icon-file'
|
||||
= tab :general_settings, :mail_methods, :tax_categories, :tax_rates, :tax_settings, :zones, :countries, :states, :payment_methods, :taxonomies, :shipping_methods, :shipping_categories, :enterprise_fees, :contents, :invoice_settings, :matomo_settings, :stripe_connect_settings, label: 'configuration', icon: 'icon-wrench', url: edit_admin_general_settings_path
|
||||
= tab :enterprises, :enterprise_relationships, :oidc_settings, url: main_app.admin_enterprises_path
|
||||
= tab :enterprises, :enterprise_relationships, :vouchers, :oidc_settings, url: main_app.admin_enterprises_path
|
||||
= tab :customers, url: main_app.admin_customers_path
|
||||
= tab :enterprise_groups, url: main_app.admin_enterprise_groups_path, label: 'groups'
|
||||
- if can? :admin, Spree::User
|
||||
|
||||
@@ -12,13 +12,40 @@ export default class extends Controller {
|
||||
|
||||
// only display the default panel
|
||||
this.defaultTarget.style.display = "block";
|
||||
|
||||
// Display panel specified in url anchor
|
||||
const anchors = window.location.toString().split("#");
|
||||
let anchor = anchors.length > 1 ? anchors.pop() : "";
|
||||
|
||||
if (anchor != "") {
|
||||
// Conveniently AngularJs rewrite "example.com#panel" to "example.com#/panel" :(
|
||||
// strip the starting / if any
|
||||
if (anchor[0] == "/") {
|
||||
anchor = anchor.slice(1);
|
||||
}
|
||||
|
||||
this.updateActivePanel(anchor);
|
||||
|
||||
// tab
|
||||
const tab_id = anchor.split("_panel").shift();
|
||||
this.updateActiveTab(tab_id);
|
||||
}
|
||||
}
|
||||
|
||||
changeActivePanel(event) {
|
||||
this.updateActivePanel(`${event.currentTarget.id}_panel`);
|
||||
}
|
||||
|
||||
updateActivePanel(panel_id) {
|
||||
const newActivePanel = this.panelTargets.find(
|
||||
(panel) => panel.id == `${event.currentTarget.id}_panel`
|
||||
(panel) => panel.id == panel_id
|
||||
);
|
||||
|
||||
if (newActivePanel === undefined) {
|
||||
// No panel found
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentActivePanel.style.display = "none";
|
||||
newActivePanel.style.display = "block";
|
||||
}
|
||||
@@ -28,6 +55,18 @@ export default class extends Controller {
|
||||
event.currentTarget.classList.add(`${this.classNameValue}`);
|
||||
}
|
||||
|
||||
updateActiveTab(tab_id) {
|
||||
const newActiveTab = this.tabTargets.find((tab) => tab.id == tab_id);
|
||||
|
||||
if (newActiveTab === undefined) {
|
||||
// No tab found
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentActiveTab.classList.remove(`${this.classNameValue}`);
|
||||
newActiveTab.classList.add(`${this.classNameValue}`);
|
||||
}
|
||||
|
||||
get currentActiveTab() {
|
||||
return this.tabTargets.find((tab) => tab.classList.contains("selected"));
|
||||
}
|
||||
|
||||
@@ -1143,6 +1143,18 @@ en:
|
||||
add_unregistered_user: "Add an unregistered user"
|
||||
email_confirmed: "Email confirmed"
|
||||
email_not_confirmed: "Email not confirmed"
|
||||
vouchers:
|
||||
legend: Vouchers
|
||||
voucher_code: Voucher Code
|
||||
rate: Rate
|
||||
label: Label
|
||||
purpose: Purpose
|
||||
expiry: Expiry
|
||||
use_limit: Use/Limit
|
||||
customers: Customer
|
||||
net_value: Net Value
|
||||
add_new: Add New
|
||||
no_voucher_yet: No Vouchers yet
|
||||
actions:
|
||||
edit_profile: Settings
|
||||
properties: Properties
|
||||
@@ -1381,6 +1393,7 @@ en:
|
||||
tag_rules: "Tag Rules"
|
||||
shop_preferences: "Shop Preferences"
|
||||
users: "Users"
|
||||
vouchers: Vouchers
|
||||
enterprise_group:
|
||||
primary_details: "Primary Details"
|
||||
users: "Users"
|
||||
@@ -1589,6 +1602,13 @@ en:
|
||||
schedules:
|
||||
destroy:
|
||||
associated_subscriptions_error: This schedule cannot be deleted because it has associated subscriptions
|
||||
vouchers:
|
||||
new:
|
||||
legend: New Voucher
|
||||
back: Back
|
||||
save: Save
|
||||
voucher_code: Voucher Code
|
||||
voucher_amount: Amount
|
||||
|
||||
# Admin controllers
|
||||
controllers:
|
||||
|
||||
@@ -40,6 +40,10 @@ Openfoodnetwork::Application.routes.draw do
|
||||
end
|
||||
|
||||
resources :tag_rules, only: [:destroy]
|
||||
|
||||
constraints FeatureToggleConstraint.new(:vouchers) do
|
||||
resources :vouchers, only: [:new, :create]
|
||||
end
|
||||
end
|
||||
|
||||
resources :enterprise_relationships
|
||||
|
||||
12
db/migrate/20230215034821_create_vouchers.rb
Normal file
12
db/migrate/20230215034821_create_vouchers.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
class CreateVouchers < ActiveRecord::Migration[6.1]
|
||||
def change
|
||||
create_table :vouchers do |t|
|
||||
t.string :code, null: false, limit: 255
|
||||
t.datetime :expiry_date
|
||||
|
||||
t.timestamps
|
||||
end
|
||||
add_reference :vouchers, :enterprise, foreign_key: true
|
||||
add_index :vouchers, [:code, :enterprise_id], unique: true
|
||||
end
|
||||
end
|
||||
11
db/schema.rb
11
db/schema.rb
@@ -1196,6 +1196,16 @@ ActiveRecord::Schema[7.0].define(version: 2023_03_15_031807) do
|
||||
t.index ["user_id"], name: "index_webhook_endpoints_on_user_id"
|
||||
end
|
||||
|
||||
create_table "vouchers", force: :cascade do |t|
|
||||
t.string "code", limit: 255, null: false
|
||||
t.datetime "expiry_date"
|
||||
t.datetime "created_at", precision: 6, null: false
|
||||
t.datetime "updated_at", precision: 6, null: false
|
||||
t.bigint "enterprise_id"
|
||||
t.index ["code", "enterprise_id"], name: "index_vouchers_on_code_and_enterprise_id", unique: true
|
||||
t.index ["enterprise_id"], name: "index_vouchers_on_enterprise_id"
|
||||
end
|
||||
|
||||
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
|
||||
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
|
||||
add_foreign_key "adjustment_metadata", "enterprises", name: "adjustment_metadata_enterprise_id_fk"
|
||||
@@ -1301,4 +1311,5 @@ ActiveRecord::Schema[7.0].define(version: 2023_03_15_031807) do
|
||||
add_foreign_key "variant_overrides", "enterprises", column: "hub_id", name: "variant_overrides_hub_id_fk"
|
||||
add_foreign_key "variant_overrides", "spree_variants", column: "variant_id", name: "variant_overrides_variant_id_fk"
|
||||
add_foreign_key "webhook_endpoints", "spree_users", column: "user_id"
|
||||
add_foreign_key "vouchers", "enterprises"
|
||||
end
|
||||
|
||||
@@ -37,6 +37,9 @@ module OpenFoodNetwork
|
||||
"split_checkout" => <<~DESC,
|
||||
Replace the one-page checkout with a multi-step checkout.
|
||||
DESC
|
||||
"vouchers" => <<~DESC,
|
||||
Add voucher functionality. Voucher can be managed via Enterprise settings.
|
||||
DESC
|
||||
}.freeze
|
||||
|
||||
# Move your feature entry from CURRENT_FEATURES to RETIRED_FEATURES when
|
||||
|
||||
@@ -2,16 +2,26 @@
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
|
||||
import { Application } from "stimulus";
|
||||
import tabs_and_panels_controller from "../../../app/webpacker/controllers/tabs_and_panels_controller";
|
||||
import { Application } from 'stimulus';
|
||||
import tabs_and_panels_controller from '../../../app/webpacker/controllers/tabs_and_panels_controller';
|
||||
|
||||
describe("EnterprisePanelController", () => {
|
||||
describe('TabsAndPanelsController', () => {
|
||||
beforeAll(() => {
|
||||
const application = Application.start();
|
||||
application.register("tabs-and-panels", tabs_and_panels_controller);
|
||||
application.register('tabs-and-panels', tabs_and_panels_controller);
|
||||
});
|
||||
|
||||
describe("#tabs-and-panels", () => {
|
||||
describe('#tabs-and-panels', () => {
|
||||
const checkDefaultPanel = () => {
|
||||
const peekPanel = document.getElementById('peek_panel');
|
||||
const kaPanel = document.getElementById('ka_panel');
|
||||
const booPanel = document.getElementById('boo_panel');
|
||||
|
||||
expect(peekPanel.style.display).toBe('block');
|
||||
expect(kaPanel.style.display).toBe('none');
|
||||
expect(booPanel.style.display).toBe('none');
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = `
|
||||
<div data-controller="tabs-and-panels" data-tabs-and-panels-class-name-value="selected">
|
||||
@@ -26,23 +36,97 @@ describe("EnterprisePanelController", () => {
|
||||
</div>`;
|
||||
});
|
||||
|
||||
it("displays only the default panel", () => {
|
||||
const peekPanel = document.getElementById("peek_panel");
|
||||
const kaPanel = document.getElementById("ka_panel");
|
||||
const booPanel = document.getElementById("boo_panel");
|
||||
|
||||
expect(peekPanel.style.display).toBe("block");
|
||||
expect(kaPanel.style.display).toBe("none");
|
||||
expect(booPanel.style.display).toBe("none");
|
||||
it('displays only the default panel', () => {
|
||||
checkDefaultPanel()
|
||||
});
|
||||
|
||||
it("displays appropriate panel when associated tab is clicked", () => {
|
||||
const kaPanel = document.getElementById("ka_panel");
|
||||
const ka = document.getElementById("ka");
|
||||
describe('when tab is clicked', () => {
|
||||
let ka;
|
||||
|
||||
expect(kaPanel.style.display).toBe("none");
|
||||
ka.click();
|
||||
expect(kaPanel.style.display).toBe("block");
|
||||
});
|
||||
beforeEach(() => {
|
||||
ka = document.getElementById('ka');
|
||||
})
|
||||
|
||||
it('displays appropriate panel', () => {
|
||||
const kaPanel = document.getElementById('ka_panel');
|
||||
|
||||
expect(kaPanel.style.display).toBe('none');
|
||||
ka.click();
|
||||
expect(kaPanel.style.display).toBe('block');
|
||||
});
|
||||
|
||||
it('selects the clicked tab', () => {
|
||||
ka.click();
|
||||
expect(ka.classList.contains('selected')).toBe(true);
|
||||
});
|
||||
|
||||
describe("when panel doesn't exist", () => {
|
||||
beforeEach(() => {
|
||||
document.body.innerHTML = `
|
||||
<div data-controller="tabs-and-panels" data-tabs-and-panels-class-name-value="selected">
|
||||
<a id="peek" href="#" data-action="tabs-and-panels#changeActivePanel tabs-and-panels#changeActiveTab" class="selected" data-tabs-and-panels-target="tab">Peek</a>
|
||||
<a id="ka" href="#" data-action="tabs-and-panels#changeActivePanel tabs-and-panels#changeActiveTab" data-tabs-and-panels-target="tab">Ka</a>
|
||||
<a id="boo" href="#" data-action="tabs-and-panels#changeActivePanel tabs-and-panels#changeActiveTab" data-tabs-and-panels-target="tab">Boo</a>
|
||||
|
||||
|
||||
<div id="peek_panel" data-tabs-and-panels-target="panel default">Peek me</div>
|
||||
<div id="boo_panel" data-tabs-and-panels-target="panel">Boo three</div>
|
||||
</div>`;
|
||||
});
|
||||
|
||||
it('displays the current panel', () => {
|
||||
const peekPanel = document.getElementById('peek_panel');
|
||||
|
||||
ka.click();
|
||||
expect(peekPanel.style.display).toBe('block');
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when anchor is specified in the url', () => {
|
||||
const { location } = window;
|
||||
const mockLocationToString = (panel) => {
|
||||
// Mocking window.location.toString()
|
||||
const url = `http://localhost:3000/admin/enterprises/great-shop/edit#/${panel}`
|
||||
const mockedToString = jest.fn()
|
||||
mockedToString.mockImplementation(() => (url))
|
||||
|
||||
delete window.location
|
||||
window.location = {
|
||||
toString: mockedToString
|
||||
}
|
||||
}
|
||||
|
||||
beforeAll(() => {
|
||||
mockLocationToString('ka_panel')
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
// cleaning up
|
||||
window.location = location
|
||||
})
|
||||
|
||||
it('displays the panel associated with the anchor', () => {
|
||||
const kaPanel = document.getElementById('ka_panel');
|
||||
|
||||
expect(kaPanel.style.display).toBe('block');
|
||||
})
|
||||
|
||||
it('selects the tab entry associated with the anchor', () => {
|
||||
const ka = document.getElementById('ka');
|
||||
|
||||
expect(ka.classList.contains('selected')).toBe(true);
|
||||
})
|
||||
|
||||
describe("when anchor doesn't macht any panel", () => {
|
||||
beforeAll(() => {
|
||||
mockLocationToString('random_panel')
|
||||
})
|
||||
|
||||
it('displays the default panel', () => {
|
||||
checkDefaultPanel()
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
@@ -24,6 +24,7 @@ describe Enterprise do
|
||||
it { is_expected.to have_many(:distributed_orders) }
|
||||
it { is_expected.to belong_to(:address) }
|
||||
it { is_expected.to belong_to(:business_address) }
|
||||
it { is_expected.to have_many(:vouchers) }
|
||||
|
||||
it "destroys enterprise roles upon its own demise" do
|
||||
e = create(:enterprise)
|
||||
@@ -740,13 +741,9 @@ describe Enterprise do
|
||||
it "assigns permalink when initialized" do
|
||||
allow(Enterprise).to receive(:find_available_permalink).and_return("available_permalink")
|
||||
expect(Enterprise).to receive(:find_available_permalink).with("Name To Turn Into A Permalink")
|
||||
expect(
|
||||
lambda { enterprise.send(:initialize_permalink) }
|
||||
).to change{
|
||||
enterprise.permalink
|
||||
}.to(
|
||||
"available_permalink"
|
||||
)
|
||||
expect do
|
||||
enterprise.send(:initialize_permalink)
|
||||
end.to change { enterprise.permalink }.to("available_permalink")
|
||||
end
|
||||
|
||||
describe "finding a permalink" do
|
||||
|
||||
@@ -788,6 +788,10 @@ describe Spree::Ability do
|
||||
is_expected.to have_ability([:admin, :known_users, :customers], for: :search)
|
||||
is_expected.not_to have_ability([:users], for: :search)
|
||||
end
|
||||
|
||||
it "has the ability to manage vouchers" do
|
||||
is_expected.to have_ability([:admin, :create], for: Voucher)
|
||||
end
|
||||
end
|
||||
|
||||
context 'enterprise owner' do
|
||||
|
||||
18
spec/models/voucher_spec.rb
Normal file
18
spec/models/voucher_spec.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe Voucher do
|
||||
describe 'associations' do
|
||||
it { is_expected.to belong_to(:enterprise) }
|
||||
end
|
||||
|
||||
describe 'validations' do
|
||||
subject { Voucher.new(code: 'new_code', enterprise: enterprise) }
|
||||
|
||||
let(:enterprise) { build(:enterprise) }
|
||||
|
||||
it { is_expected.to validate_presence_of(:code) }
|
||||
it { is_expected.to validate_uniqueness_of(:code).scoped_to(:enterprise_id) }
|
||||
end
|
||||
end
|
||||
78
spec/system/admin/vouchers_spec.rb
Normal file
78
spec/system/admin/vouchers_spec.rb
Normal file
@@ -0,0 +1,78 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'system_helper'
|
||||
|
||||
describe '
|
||||
As an entreprise user
|
||||
I want to manage vouchers
|
||||
' do
|
||||
include WebHelper
|
||||
include AuthenticationHelper
|
||||
|
||||
let(:enterprise) { create(:supplier_enterprise, name: 'Feedme') }
|
||||
let(:voucher_code) { 'awesomevoucher' }
|
||||
let(:enterprise_user) { create(:user, enterprise_limit: 1) }
|
||||
|
||||
before do
|
||||
Flipper.enable(:vouchers)
|
||||
|
||||
enterprise_user.enterprise_roles.build(enterprise: enterprise).save
|
||||
login_as enterprise_user
|
||||
end
|
||||
|
||||
it 'lists enterprise vouchers' do
|
||||
# Given an enterprise with vouchers
|
||||
Voucher.create!(enterprise: enterprise, code: voucher_code)
|
||||
|
||||
# When I go to the enterprise voucher tab
|
||||
visit edit_admin_enterprise_path(enterprise)
|
||||
|
||||
click_link 'Vouchers'
|
||||
|
||||
# Then I see a list of vouchers
|
||||
expect(page).to have_content voucher_code
|
||||
expect(page).to have_content "10"
|
||||
end
|
||||
|
||||
it 'creates a voucher' do
|
||||
# Given an enterprise
|
||||
# When I go to the enterprise voucher tab and click new
|
||||
visit edit_admin_enterprise_path(enterprise)
|
||||
|
||||
click_link 'Vouchers'
|
||||
within "#vouchers_panel" do
|
||||
click_link 'Add New'
|
||||
end
|
||||
|
||||
# And I fill in the fields for a new voucher click save
|
||||
fill_in 'voucher_code', with: voucher_code
|
||||
click_button 'Save'
|
||||
|
||||
# Then I should get redirect to the entreprise voucher tab and see the created voucher
|
||||
expect(page).to have_selector '.success', text: 'Voucher has been successfully created!'
|
||||
expect(page).to have_content voucher_code
|
||||
expect(page).to have_content "10"
|
||||
|
||||
voucher = Voucher.where(enterprise: enterprise, code: voucher_code).first
|
||||
|
||||
expect(voucher).not_to be(nil)
|
||||
end
|
||||
|
||||
context 'when entering invalid data' do
|
||||
it 'shows an error flash message' do
|
||||
# Given an enterprise
|
||||
# When I go to the new voucher page
|
||||
visit new_admin_enterprise_voucher_path(enterprise)
|
||||
|
||||
# And I fill in fields with invalid data and click save
|
||||
click_button 'Save'
|
||||
|
||||
# Then I should see an error flash message
|
||||
expect(page).to have_selector '.error', text: "Code can't be blank"
|
||||
|
||||
vouchers = Voucher.where(enterprise: enterprise)
|
||||
|
||||
expect(vouchers).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user