<repo_name>faalbuquerque/my-promotion-system<file_sep>/db/migrate/20210214045344_create_category_promotions.rb
class CreateCategoryPromotions < ActiveRecord::Migration[6.1]
  def change
    create_table :category_promotions do |t|
      t.integer :promotion_id
      t.integer :category_id
      t.timestamps
    end
  end
end
<file_sep>/spec/requests/coupon_management_spec.rb
require 'rails_helper'

describe 'Coupon management' do
  context 'GET coupon' do

    it 'should render coupon information' do
      creator = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
      promotion = Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                                    code: 'NATAL10', discount_rate: 10, coupon_quantity: 1,
                                    expiration_date: '22/12/2033', admin: creator)
      promotion.create_coupons!
      coupon = promotion.coupons.last

      get "/api/v1/coupons/#{ coupon.code }"

      json_response = JSON.parse(response.body, symbolize_names: true)

      expect(response).to have_http_status(200)
      expect(json_response[:status]).to eq('active')
      expect(json_response[:promotion][:discount_rate]).to eq('10.0')
    end

    it 'should return 404, coupon does not exist' do
      get "/api/v1/coupons/naoexiste"

      expect(response).to have_http_status :not_found
    end

  end
end
<file_sep>/README.md
## Projeto TreinaDev 05 - Primeira Etapa

Desenvolvimento de um sistema em Ruby on Rails como parte do treinamento da Campus Code.  

### Tecnologias:
- Ruby
- Rails
- RSpec
- I18n

### [Link: Trello](https://trello.com/b/zehBJt2P/treinadev-atividades-6)
<file_sep>/spec/features/admin_view_categories_spec.rb
require 'rails_helper'

feature 'Admin view categories' do
  scenario 'successfully' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin

    Category.create!(name: 'Filmes de Suspense', code: 'FILMESS')
    Category.create!(name: 'Filmes de Medo', code: 'FILMESM', )

    visit root_path
    click_on 'Categorias'

    expect(page).to have_content('Filmes de Suspense')
    expect(page).to have_content('FILMESS')
    expect(page).to have_content('Filmes de Medo')
    expect(page).to have_content('FILMESM')
  end

  scenario 'and return to home page' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin

    Category.create!(name: 'Games', code: 'games100')

    visit root_path
    click_on 'Categorias'
    click_on 'Voltar'

    expect(current_path).to eq root_path
  end

  scenario 'and view details' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin

    Category.create!(name: 'Gatos', code: 'gatospeludos')
    Category.create!(name: 'Caes', code: 'Caesfofinhos')

    visit root_path
    click_on 'Categorias'
    click_on 'Gatos'

    expect(page).to have_content('Gatos')
    expect(page).to have_content('gatospeludos')
  end

  scenario 'and return to categories page' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin
    
    Category.create!(name: 'Game of Thrones', code: 'GOT')

    visit root_path
    click_on 'Categorias'
    click_on 'Game of Thrones'
    click_on 'Voltar'

    expect(current_path).to eq categories_path
  end

  scenario 'failure, not signed in' do

    Category.create!(name: 'Gatos', code: 'gatospeludos')
    Category.create!(name: 'Caes', code: 'Caesfofinhos')

    visit categories_path

    expect(page).to_not have_content('Gatos')
    expect(page).to_not have_content('gatospeludos')
  end
end
<file_sep>/spec/features/admin_edit_categories_spec.rb
require 'rails_helper'

feature 'Admin edit categories' do
  scenario 'successfully' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin

    Category.create!(name: 'Comidinha', code: 'comi100')
    Category.first.update!(name: 'Comida japonesa', code: 'comijapa')

    visit root_path
    click_on 'Categorias'

    expect(page).to_not have_content('Comidinha')
    expect(page).to_not have_content('comi100')
    expect(page).to have_content('Comida japonesa')
    expect(page).to have_content('comijapa')
  end

  scenario 'and attributes cannot be blank' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin

    Category.create!(name: 'Bebida', code: 'bebi100')

    visit root_path
    click_on 'Categorias'
    click_on 'Editar categoria'
    fill_in 'Nome', with: ''
    fill_in 'Código', with: ''
    click_on 'Atualizar categoria'

    p page.html
    expect(page).to have_content('não pode ficar em branco', count: 2)
  end

  scenario 'and code must be unique' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin
    
    Category.create!(name: 'Caes', code: 'caes10')
    Category.create!(name: 'Gatos', code: 'gatos10')

    visit edit_category_path(Category.last)
    fill_in 'Código', with: 'caes10'
    click_on 'Atualizar categoria'

    expect(page).to have_content('já está em uso')
  end
end
<file_sep>/spec/models/promotion_approval_spec.rb
require 'rails_helper'

RSpec.describe PromotionApproval, type: :model do
  describe '#valid?' do
    describe "different_admin" do
      it 'is different' do
        creator = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
        approval_admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")

        promotion = Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                                      code: 'NATAL10', discount_rate: 10, coupon_quantity: 100,
                                      expiration_date: '22/12/2033', admin: creator)

        approval = PromotionApproval.new(promotion: promotion, admin: approval_admin)

        expect(approval.valid?).to eq true
      end

      it 'is the same' do
        creator = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
        approval_admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")

        promotion = Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                                      code: 'NATAL10', discount_rate: 10, coupon_quantity: 100,
                                      expiration_date: '22/12/2033', admin: creator)

        approval = PromotionApproval.new(promotion: promotion, admin: creator)

        expect(approval.valid?).to eq false
      end

      it 'has no promotion or admin' do
        approval = PromotionApproval.new()

        expect(approval.valid?).to eq false
      end
    end
  end
end
<file_sep>/spec/features/admin_approves_a_promotion_spec.rb
require 'rails_helper'

feature 'Admin approves a promotion' do
  scenario 'successfully' do
    creator = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    approval_admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")

    promotion = Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                                  code: 'NATAL10', discount_rate: 10, coupon_quantity: 100,
                                  expiration_date: '22/12/2033', admin: creator)

    sign_in approval_admin
    visit promotion_path(promotion)
    click_on 'Aprovar promoção'

    promotion.reload
    expect(current_path).to eq promotion_path(promotion)
    expect(promotion.approved?).to be_truthy
    expect(promotion.approver).to eq approval_admin
    expect(page).to have_content 'Status: Aprovada!'

  end

  scenario 'must not be the promotion creator' do
    creator = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")

    promotion = Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                                  code: 'NATAL10', discount_rate: 10, coupon_quantity: 100,
                                  expiration_date: '22/12/2033', admin: creator)


    sign_in creator
    visit promotion_path(promotion)
    expect(page).to_not have_link 'Aprovar promoção'
  end

  scenario 'must be another user' do
    creator = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    approval_user = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")

    promotion = Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                                  code: 'NATAL10', discount_rate: 10, coupon_quantity: 100,
                                  expiration_date: '22/12/2033', admin: creator)

    sign_in approval_user

    visit promotion_path(promotion)
    expect(page).to have_link 'Aprovar promoção'
  end
end<file_sep>/spec/features/admin_register_product_category_spec.rb
require 'rails_helper'

feature 'Admin registers a product category' do
  scenario 'and attributes cannot be blank' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin

    visit root_path
    click_on 'Categorias'
    click_on 'Registrar uma categoria'
    fill_in 'Nome', with: ''
    fill_in 'Código', with: ''
    click_on 'Criar categoria'

    expect(Category.count).to eq 0
    expect(page).to have_content('Nome não pode ficar em branco')
    expect(page).to have_content('Código não pode ficar em branco')
  end

  scenario 'and code must be unique' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin
    
    Category.create!(name: 'Filmes', code: 'FILMES10')

    visit root_path
    click_on 'Categorias'
    click_on 'Registrar uma categoria'
    fill_in 'Código', with: 'FILMES10'
    click_on 'Criar categoria'

    expect(page).to have_content('já está em uso')
  end

end
<file_sep>/app/models/promotion_approval.rb
class PromotionApproval < ApplicationRecord
  belongs_to :promotion
  belongs_to :admin

  validate :different_admin

  private

  def different_admin
    if promotion && promotion.admin == admin
      errors.add(:admin, 'nao pode ser o criador da promocao') 
    end
  end 
end
<file_sep>/app/models/promotion.rb
class Promotion < ApplicationRecord
  has_many :coupons, dependent: :destroy
  has_one :promotion_approval
  belongs_to :admin

  has_many :category_promotions
  has_many :categories, through: :category_promotions

  validates :name, :code, presence: true, uniqueness: true
  validates :discount_rate, :coupon_quantity, :expiration_date, presence: true

  def create_coupons!
    Coupon.transaction do
      1.upto(coupon_quantity) do |num|
        #transformar num array de hashes e fazer um insert all
        coupons.create!(code: "#{ code }-#{ num.to_s.rjust(4, '0') }")
      end
    end
  end

  def approve!(approval_admin)
    PromotionApproval.create(promotion: self, admin: approval_admin)
  end

  def approved?
    promotion_approval
  end

  def approver
    promotion_approval.admin
  end
end
<file_sep>/spec/models/category_spec.rb
require 'rails_helper'

RSpec.describe Category, type: :model do
  context 'validation' do
    it { should validate_presence_of(:name) }
    it { should validate_presence_of(:code) }
    
    it { should validate_uniqueness_of(:code) }
  end
end
<file_sep>/spec/models/promotion_spec.rb
require 'rails_helper'

describe Promotion do
  context 'validation' do
    it 'attributes cannot be blank' do
      admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")

      promotion = Promotion.new(name: '', description: '', code: '', 
                                discount_rate: '', coupon_quantity: '',
                                expiration_date: '', admin: admin)

      expect(promotion.valid?).to eq false
      expect(promotion.errors.count).to eq 5
    end

    it 'description is optional' do
      admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
      promotion = Promotion.new(name: 'Natal', description: '', code: 'NAT',
                                coupon_quantity: 10, discount_rate: 10,
                                expiration_date: '2021-10-10', admin: admin)

      expect(promotion.valid?).to eq true
    end

    it 'error messages are in portuguese' do
      admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
      sign_in admin
      promotion = Promotion.new

      promotion.valid?

      expect(promotion.errors[:name]).to include('não pode ficar em branco')
      expect(promotion.errors[:code]).to include('não pode ficar em branco')
      expect(promotion.errors[:discount_rate]).to include('não pode ficar em '\
                                                          'branco')
      expect(promotion.errors[:coupon_quantity]).to include('não pode ficar em'\
                                                            ' branco')
      expect(promotion.errors[:expiration_date]).to include('não pode ficar em'\
                                                            ' branco')
    end

    it 'code must be uniq' do
      admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
      Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                        code: 'NATAL10', discount_rate: 10,
                        coupon_quantity: 100, expiration_date: '22/12/2033', admin: admin)
      promotion = Promotion.new(code: 'NATAL10')

      promotion.valid?

      expect(promotion.errors[:code]).to include('já está em uso')
    end
  end

  context '#create_coupons!' do
    it 'create coupons with a quantity' do
      admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
      promotion = Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                                    code: 'NATAL10', discount_rate: 10, coupon_quantity: 100,
                                    expiration_date: '22/12/2033', admin: admin)

      promotion.create_coupons!

      expect(promotion.coupons.size).to eq 100
      codes = promotion.coupons.pluck(:code)
      expect(codes).to include('NATAL10-0001')
      expect(codes).to include('NATAL10-0100')
      expect(codes).to_not include('NATAL10-0101')
      expect(codes).to_not include('NATAL10-0000')
    end
    it 'do not create repeated codes' do
      admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
      promotion = Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                                    code: 'NATAL10', discount_rate: 10, coupon_quantity: 100,
                                    expiration_date: '22/12/2033', admin: admin)
      promotion.coupons.create!(code: 'NATAL10-0030')

      expect { promotion.create_coupons! }.to raise_error(ActiveRecord::RecordInvalid)

      expect(promotion.coupons.reload.size).to eq(1)
    end
  end

  context '#approve!' do
    it 'should generate a PromotionApproval object' do
      creator = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
      approval_admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")

      promotion = Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                                    code: 'NATAL10', discount_rate: 10, coupon_quantity: 100,
                                    expiration_date: '22/12/2033', admin: creator)

      promotion.approve!(approval_admin)
      promotion.reload

      expect(promotion.approved?).to be_truthy
      expect(promotion.approver).to eq approval_admin
    end

    it 'it should not approve if same admin ' do
      creator = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")

      promotion = Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                                    code: 'NATAL10', discount_rate: 10, coupon_quantity: 100,
                                    expiration_date: '22/12/2033', admin: creator)

      promotion.approve!(creator)
      promotion.reload

      expect(promotion.approved?).to be_falsy
    end
  end
end
<file_sep>/app/models/category_promotion.rb
class CategoryPromotion < ApplicationRecord
  belongs_to :promotion
  belongs_to :category
end
<file_sep>/spec/features/admin_search_for_promotion_and_coupons.rb
require 'rails_helper'

feature 'Admin search for promotion and coupons' do
  scenario 'successfully' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin

    promotion_natal = Promotion.create!(name: 'Natal 2020', description: 'Promoção de Natal',
                                        code: 'NATAL20', discount_rate: 5, coupon_quantity: 10, expiration_date: '22/12/2031', admin: admin)
    promotion_ano = Promotion.create!(name: 'Ano novo', description: 'Promoção de Ano novo',
                                      code: 'ANONOVO', discount_rate: 15, coupon_quantity: 30,
                                      expiration_date: '22/12/2033', admin: admin)

    Coupon.create!(code: 'NATAL-20', promotion: promotion_natal)
    Coupon.create!(code: 'NOVO-20', promotion: promotion_ano)

    visit root_path
    click_on 'Promoções'

    fill_in 'Buscar', with: 'natal'
    click_on 'Pesquisar'

    expect(current_path).to eq search_path
    expect(page).to have_content('Natal')
    expect(page).to_not have_content('Ano novo')
  end

  scenario 'result not found' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin

    promotion_natal21 = Promotion.create!(name: 'Natal 2021', coupon_quantity: 20,
                                          description: 'Promoção de Natal de novo',
                                          code: 'NATAL21', discount_rate: 10,
                                          expiration_date: '22/12/2032', admin: admin)
    promotion_ano_novo = Promotion.create!(name: 'Ano novo', description:'Anonovo'
                                           code:'ANONOVO', discount_rate: 15,
                                           coupon_quantity: 30, expiration_date: '22/12/2033', admin: admin)

    Coupon.create!(code: 'NATAL21', promotion: promotion_natal21)
    Coupon.create!(code: 'ANONOVO', promotion: promotion_ano_novo)

    visit root_path
    click_on 'Promoções'

    fill_in 'Buscar', with: 'Cachorro'
    click_on 'Pesquisar'

    expect(current_path).to eq search_path
    expect(page).to have_content('Nenhum resultado encontrado!')
    expect(page).to_not have_content('Natal')
    expect(page).to_not have_content('Ano novo')
  end
end
<file_sep>/spec/features/admin_delete_promotions_spec.rb
require 'rails_helper'

feature 'Admin delete promotions' do
  scenario 'successfully' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin
    
    Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                      code: 'NATAL50', discount_rate: 10, coupon_quantity: 100,
                      expiration_date: '22/12/2033', admin: admin)
    Promotion.create!(name: 'Natal1', description: 'Promoção de Natal1',
                      code: 'NATAL501', discount_rate: 101, coupon_quantity: 1001,
                      expiration_date: '22/12/2031', admin: admin)

    visit promotion_path(Promotion.last)
    click_on 'Apagar promoção'

    expect(page).to have_content('Natal')
    expect(page).to have_content('Promoção de Natal')
    expect(page).to have_content('NATAL50')
    expect(page).to have_content('10,00%')
    expect(page).to have_content('100')
    expect(page).to have_content('22/12/2033')

    expect(page).to_not have_content('Natal1')
    expect(page).to_not have_content('Promoção de Natal1')
    expect(page).to_not have_content('NATAL501')
    expect(page).to_not have_content('101,00%')
    expect(page).to_not have_content('1001')
    expect(page).to_not have_content('22/12/2031')
  end
end
<file_sep>/spec/features/admin_registers_and_login_in_the_application_spec.rb
require 'rails_helper'

feature 'Admin in the application' do

  scenario 'register' do
    visit root_path
    click_on 'Login'
    click_on 'Sign up'

    fill_in 'E-mail', with: '<EMAIL>'
    fill_in 'Senha', with: '<PASSWORD>'
    fill_in 'Password confirmation', with: '<PASSWORD>'
    click_on 'Sign up'

    expect(page).to have_content('<EMAIL>')
    expect(page).to have_content('Promoções')
  end

  scenario 'login successfully' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")

    visit root_path
    click_on 'Login'

    fill_in 'E-mail', with: '<EMAIL>'
    fill_in 'Senha', with: '<PASSWORD>'
    click_on 'Log in'

    expect(page).to have_content(admin.email)
    expect(page).to have_content('Logout')
  end

  scenario 'logout successfully' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")

    visit root_path
    click_on 'Login'

    fill_in 'E-mail', with: '<EMAIL>'
    fill_in 'Senha', with: '<PASSWORD>'
    click_on 'Log in'
    click_on 'Logout'

    expect(page).to_not have_content(admin.email)
    expect(page).to have_content('Login')
  end
end
<file_sep>/spec/features/admin_activate_coupon_spec.rb
require 'rails_helper'

feature 'Admin activate coupons' do
  scenario 'successfully' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin

    promotion = Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                                  code: 'NATAL10', discount_rate: 10, coupon_quantity: 100,
                                  expiration_date: '22/12/2033', admin: admin)
    coupon = Coupon.create!(code: 'AAA-11', promotion: promotion)

    visit root_path
    click_on 'Promoções'
    click_on promotion.name
    click_on 'Inativar'
 
    coupon.reload
    expect(page).to  have_content('AAA-11(Inativo)')

    #expect(coupon).to be_inactive
    expect(coupon.inactive?).to be(true)
  end

  scenario 'do not view the button to activate coupons' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin
    
    promotion = Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                                  code: 'NATAL10', discount_rate: 10, coupon_quantity: 2,
                                  expiration_date: '22/12/2033', admin: admin)
    inactive_coupon = Coupon.create!(code: 'ABC0001', promotion: promotion, status: :inactive)
    active_coupon = Coupon.create!(code: 'ABC0002', promotion: promotion, status: :active)

    visit root_path
    click_on 'Promoções'
    click_on promotion.name

    expect(page).to have_content('ABC0001(Inativo)')
    expect(page).to have_content('ABC0002(Ativo)')

    within("div#coupon-#{active_coupon.id}") do
      expect(page).to have_link 'Inativar'
    end

    within("div#coupon-#{inactive_coupon.id}") do
      expect(page).not_to have_link 'Inativar'
    end
  end
end
<file_sep>/app/controllers/categories_controller.rb
class CategoriesController < ApplicationController
  before_action :authenticate_admin!
  before_action :find_category, only: %i[show edit update destroy]

  def index
    @categories = Category.all
  end

  def new
    @category = Category.new
  end

  def create
    @category = Category.new(category_params)
    return redirect_to @category if @category.save

    respond_to do |format|
      format.js { render partial: 'message' }
      format.html { render partial: 'result_message'}
    end
    #render :new
  end

  def show
  end

  def edit
  end

  def update
    return redirect_to @category if @category.update(category_params)

    respond_to do |format|
      format.js { render partial: 'message' }
      format.html { render partial: 'result_message'}
    end
    #render :edit
  end

  def destroy
    return redirect_to categories_path if @category.delete

    render :index
  end

  private

  def category_params
    params.require(:category).permit(:name, :code)
  end

  def find_category
    @category = Category.find(params[:id])
  end
end
<file_sep>/spec/features/admin_edit_promotions_spec.rb
require 'rails_helper'

feature 'Admin edit promotions' do
  scenario 'successfully' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin

    Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                      code: 'NATAL10', discount_rate: 10, coupon_quantity: 100,
                      expiration_date: '22/12/2033', admin: admin)
    Promotion.first.update!(name: 'Natal 2034', description: 'Promoção de Natal 2034', 
                            coupon_quantity: 34, code: 'NATAL34', discount_rate: 34, 
                            expiration_date: '22/12/2034', admin: admin)

    visit root_path
    click_on 'Promoções'

    expect(page).to have_content('Natal 2034')
    expect(page).to have_content('Promoção de Natal 2034')
    expect(page).to have_content(34)
    expect(page).to have_content('NATAL34')
    expect(page).to have_content('34,00%')
    expect(page).to have_content('22/12/2034')
  end

  scenario 'and attributes cannot be blank' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin

    Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                      code: 'NATAL10', discount_rate: 10, coupon_quantity: 100,
                      expiration_date: '22/12/2033', admin: admin)
  
    visit root_path
    click_on 'Promoções'
    click_on 'Editar promoção'
    fill_in 'Nome', with: ''
    fill_in 'Descrição', with: ''
    fill_in 'Código', with: ''
    fill_in 'Desconto', with: ''
    fill_in 'Quantidade de cupons', with: ''
    fill_in 'Data de término', with: ''
    click_on 'Atualizar promoção'

    expect(page).to have_content('não pode ficar em branco', count: 5)
  end

  scenario 'and code must be unique' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin
    
    Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                      code: 'NATAL50', discount_rate: 10, coupon_quantity: 100,
                      expiration_date: '22/12/2033', admin: admin)
    Promotion.create!(name: 'Natal2', description: 'Promoção de Natal',
                      code: 'NATAL59', discount_rate: 10, coupon_quantity: 100,
                      expiration_date: '22/12/2033',admin: admin)

    visit edit_promotion_path(Promotion.last)
    fill_in 'Código', with: 'NATAL50'
    click_on 'Atualizar promoção'

    expect(page).to have_content('já está em uso')
  end
end
<file_sep>/app/controllers/coupons_controller.rb
class CouponsController < ApplicationController
  before_action :authenticate_admin!

  def inactivate
    @coupon = Coupon.find(params[:id])
    @coupon.inactive!

    redirect_to @coupon.promotion
  end

  def activate
    @coupon = Coupon.find(params[:id])
    @coupon.active!

    redirect_to @coupon.promotion
  end
end
<file_sep>/app/controllers/promotions_controller.rb
class PromotionsController < ApplicationController
  before_action :authenticate_admin!
  before_action :find_promotion, except: %i[index new create search]

  def index
    @promotions = Promotion.all
  end

  def new
    @promotion = Promotion.new
    @categories = Category.all.order(name: :asc)
  end

  def create

    @promotion = Promotion.create(promotion_params)
    @promotion.admin = current_admin
    
    return redirect_to @promotion if @promotion.save

    respond_to do |format|
      format.js { render partial: 'message' }
      format.html { render partial: 'result_message'}
    end

    # @categories = Category.all.order(name: :asc)
    # render :new
  end

  def show
  end

  def edit
    @categories = Category.all.order(name: :asc)
  end

  def update
    return redirect_to @promotion if @promotion.update(promotion_params)

    respond_to do |format|
      format.js { render partial: 'message' }
      format.html { render partial: 'result_message'}
    end

    #@categories = Category.all.order(name: :asc)
    #render :edit
  end

  def destroy
    return redirect_to promotions_path if @promotion.destroy!

    render promotions_path
  end

  def creates_coupons
    return redirect_to @promotion, notice: t('.burned') if @promotion.coupons.any?
    @promotion.create_coupons!

    redirect_to @promotion, notice: t('.success')
  end

  def approve
    find_promotion.approve!(current_admin)
    redirect_to find_promotion
  end

  def search
    @result_set = Promotion.where('name like ? OR description like ?', 
                                  "%#{params[:q]}%",
                                  "%#{params[:q]}%")
    @result_set += (Coupon.where('code like ?', "%#{params[:q]}%"))
  end

  private

  def promotion_params
    params.require(:promotion).permit(:name, :description, :code, :discount_rate,
                                      :coupon_quantity, :expiration_date, category_ids: [] )
  end

  def find_promotion
    @promotion = Promotion.find(params[:id])
  end
end
<file_sep>/spec/features/admin_delete_categories_spec.rb
require 'rails_helper'

feature 'Admin delete categories' do
  scenario 'successfully' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin
    
    Category.create!(name: 'Eletronicos', code: '555')
    Category.create!(name: 'Portateis', code: '666')

    visit category_path(Category.last)
    click_on 'Apagar categoria'

    expect(page).to have_content('Eletronicos')
    expect(page).to have_content('555')
    expect(page).to_not have_content('Portateis')
    expect(page).to_not have_content('666')
  end
end
<file_sep>/spec/features/user_visit_home_page_spec.rb
require 'rails_helper'

feature 'User visit home page' do
  scenario 'successfully' do
    visit root_path

    expect(page).to have_content('Promotion System')
    expect(page).to have_content('Boas vindas ao sistema de gestão de '\
                                 'promoções')
  end
end
<file_sep>/db/migrate/20210210045743_add_admin_ref_to_promotion.rb
class AddAdminRefToPromotion < ActiveRecord::Migration[6.1]
  def change
    add_reference :promotions, :admin, null: false, foreign_key: true
  end
end
<file_sep>/app/controllers/api/v1/coupons_controller.rb
class Api::V1::CouponsController < ApplicationController
  def show
    @coupon = Coupon.find_by(code: params[:id])

    return render status: 404, json: "{ message: 'Coupon not found' }" if @coupon.nil?

    render json: @coupon.as_json(only: [:code, :status],
                                 include: {
                                    promotion: { only: :discount_rate }
                                 }), status: 200
  end
end
<file_sep>/config/routes.rb
Rails.application.routes.draw do

  root 'home#index'

  devise_for :admins

  resources :categories

  resources :promotions do
    member do 
      post 'creates_coupons'
      post 'approve'
    end
  end

  get 'search', to: 'promotions#search'

  resources :coupons, only: [] do
    member do
      post 'inactivate'
      post 'activate'
    end
  end

  namespace 'api', defaults: { format: :json } do
    namespace 'v1' do
      resources :coupons, only: [:show]
    end
  end
end
<file_sep>/spec/features/admin_creates_coupons_spec.rb
require 'rails_helper'

feature 'Admin creates coupons' do

  scenario 'in a promotion' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin

    promotion = Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                                  code: 'NATAL10', discount_rate: 10, coupon_quantity: 100,
                                  expiration_date: '22/12/2033', admin: admin )

    visit root_path
    click_on 'Promoções'
    click_on promotion.name
    click_on 'Gerar cupons'

    expect(page).to have_current_path(promotion_path(promotion))
    expect(page).to have_content('Cupons gerados com sucesso!')
    expect(page).to have_content('NATAL10-0001')
    expect(page).to have_content('NATAL10-0002')
    expect(page).to have_content('NATAL10-0100')
    expect(page).to_not have_content('NATAL10-0101')
  end

  scenario 'do not view the button to generate coupons' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin
    
    promotion = Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                                  code: 'NATAL10', discount_rate: 10, coupon_quantity: 2,
                                  expiration_date: '22/12/2033', admin: admin)

    #active_coupon = Coupon.create!(code: 'ABC', promotion: promotion, status: :active)
    active_coupon = promotion.coupons.create!

    visit promotion_path(promotion)

    expect(page).to_not have_link 'Gerar cupons'
  end
end
<file_sep>/app/models/coupon.rb
class Coupon < ApplicationRecord
  belongs_to :promotion

  validates :code, uniqueness: true

  enum status: { active: 0, inactive: 10 }
end
<file_sep>/spec/features/admin_registers_a_promotion_spec.rb
require 'rails_helper'

feature 'Admin registers a promotion' do
  scenario 'from index page' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin

    visit root_path
    click_on 'Promoções'

    expect(page).to have_link('Registrar uma promoção',
                              href: new_promotion_path)
  end

  scenario 'successfully' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin
    
    visit root_path
    click_on 'Promoções'
    click_on 'Registrar uma promoção'

    fill_in 'Nome', with: 'Cyber Monday'
    fill_in 'Descrição', with: 'Promoção de Cyber Monday'
    fill_in 'Código', with: 'CYBER15'
    fill_in 'Desconto', with: '15'
    fill_in 'Quantidade de cupons', with: '90'
    fill_in 'Data de término', with: '22/12/2033'
    click_on 'Criar promoção'

    expect(current_path).to eq(promotion_path(Promotion.last))
    expect(page).to have_content('Cyber Monday')
    expect(page).to have_content('Promoção de Cyber Monday')
    expect(page).to have_content('15,00%')
    expect(page).to have_content('CYBER15')
    expect(page).to have_content('22/12/2033')
    expect(page).to have_content('90')
    expect(page).to have_content 'Criada por: <EMAIL>'
    expect(page).to have_link('Voltar')
  end

  scenario 'and choose categories' do
    Category.create!(name: 'Mouses', code: 'mouse')
    Category.create!(name: 'Jogos', code: 'jogo')
    Category.create!(name: 'Monitores', code: 'monitor')
    Category.create!(name: 'Teclados', code: 'teclado')

    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin
    
    visit root_path
    click_on 'Promoções'
    click_on 'Registrar uma promoção'

    fill_in 'Nome', with: '<NAME>'
    fill_in 'Descrição', with: 'Promoção de Cyber Monday'
    fill_in 'Código', with: 'CYBER15'
    fill_in 'Desconto', with: '15'
    fill_in 'Quantidade de cupons', with: '90'
    fill_in 'Data de término', with: '22/12/2033'

    check 'Mouses'
    check 'Jogos'
    check 'Monitores'

    click_on 'Criar promoção'

    expect(current_path).to eq(promotion_path(Promotion.last))
    expect(page).to have_content('Mouses')
    expect(page).to have_content('Jogos')
    expect(page).to have_content('Monitores')
    expect(page).to_not have_content('Teclados')
  end
end
<file_sep>/spec/features/admin_registers_a_valid_promotion_spec.rb
require 'rails_helper'

feature 'Admin registers a valid promotion' do
  scenario 'and attributes cannot be blank' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin

    visit root_path
    click_on 'Promoções'
    click_on 'Registrar uma promoção'
    fill_in 'Nome', with: ''
    fill_in 'Descrição', with: ''
    fill_in 'Código', with: ''
    fill_in 'Desconto', with: ''
    fill_in 'Quantidade de cupons', with: ''
    fill_in 'Data de término', with: ''
    click_on 'Criar promoção'

    expect(Promotion.count).to eq 0
    expect(page).to have_content('Não foi possível criar a promoção')
    expect(page).to have_content('Nome não pode ficar em branco')
    expect(page).to have_content('Código não pode ficar em branco')
    expect(page).to have_content('Desconto não pode ficar em branco')
    expect(page).to have_content('Quantidade de cupons não pode ficar em branco')
    expect(page).to have_content('Data de término não pode ficar em branco')
  end

  scenario 'and code must be unique' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin

    Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                      code: 'NATAL10', discount_rate: 10, coupon_quantity: 100,
                      expiration_date: '22/12/2033', admin: admin)

    visit root_path
    click_on 'Promoções'
    click_on 'Registrar uma promoção'
    fill_in 'Código', with: 'NATAL10'
    click_on 'Criar promoção'

    expect(page).to have_content('já está em uso')
  end

  scenario 'and name must be unique' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin
    
    Promotion.create!(name: 'Natal1', description: 'Promoção',
                      code: 'NATAL', discount_rate: 11, coupon_quantity: 101,
                      expiration_date: '22/12/2033', admin: admin)

    visit root_path
    click_on 'Promoções'
    click_on 'Registrar uma promoção'
    fill_in 'Nome', with: 'Natal1'
    click_on 'Criar promoção'

    expect(page).to have_content('já está em uso')
  end
end

<file_sep>/spec/features/admin_view_promotions_spec.rb
require 'rails_helper'

feature 'Admin view promotions' do
  scenario 'successfully' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin

    Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                      code: 'NATAL10', discount_rate: 10, coupon_quantity: 100,
                      expiration_date: '22/12/2033', admin: admin)
    Promotion.create!(name: 'Cyber Monday', coupon_quantity: 100,
                      description: 'Promoção de Cyber Monday',
                      code: 'CYBER15', discount_rate: 15,
                      expiration_date: '22/12/2033', admin: admin)

    visit root_path
    click_on 'Promoções'

    expect(page).to have_content('Natal')
    expect(page).to have_content('Promoção de Natal')
    expect(page).to have_content('10,00%')
    expect(page).to have_content('Cyber Monday')
    expect(page).to have_content('Promoção de Cyber Monday')
    expect(page).to have_content('15,00%')
  end

  scenario 'and view details' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin

    Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                      code: 'NATAL10', discount_rate: 10, coupon_quantity: 100,
                      expiration_date: '22/12/2033', admin: admin)
    Promotion.create!(name: 'Cyber Monday', coupon_quantity: 90,
                      description: 'Promoção de Cyber Monday',
                      code: 'CYBER15', discount_rate: 15,
                      expiration_date: '22/12/2033', admin: admin)

    visit root_path
    click_on 'Promoções'
    click_on 'Cyber Monday'

    expect(page).to have_content('Cyber Monday')
    expect(page).to have_content('Promoção de Cyber Monday')
    expect(page).to have_content('15,00%')
    expect(page).to have_content('CYBER15')
    expect(page).to have_content('22/12/2033')
    expect(page).to have_content('90')
  end

  scenario 'and no promotion are created' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin

    visit root_path
    click_on 'Promoções'

    expect(page).to have_content('Nenhuma promoção cadastrada')
  end

  scenario 'and return to home page' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin

    Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                      code: 'NATAL10', discount_rate: 10, coupon_quantity: 100,
                      expiration_date: '22/12/2033', admin: admin)

    visit root_path
    click_on 'Promoções'
    click_on 'Voltar'

    expect(current_path).to eq root_path
  end

  scenario 'and return to promotions page' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    sign_in admin
    
    Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                      code: 'NATAL10', discount_rate: 10, coupon_quantity: 100,
                      expiration_date: '22/12/2033', admin: admin)

    visit root_path
    click_on 'Promoções'
    click_on 'Natal'
    click_on 'Voltar'

    expect(current_path).to eq promotions_path
  end

  scenario 'and must be signed in' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    promotion = Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                                  code: 'NATAL10', discount_rate: 10, coupon_quantity: 100,
                                  expiration_date: '22/12/2033', admin: admin)

    visit promotion_path(promotion)
    expect(current_path).to eq new_admin_session_path
  end

  scenario 'failure, not signed in' do
    admin = Admin.create!(email: '<EMAIL>', password: "<PASSWORD>")
    Promotion.create!(name: 'Natal', description: 'Promoção de Natal',
                      code: 'NATAL10', discount_rate: 10, coupon_quantity: 100,
                      expiration_date: '22/12/2033', admin: admin)

    visit root_path
    click_on 'Promoções'

    expect(current_path).to_not eq promotions_path
    expect(page).to_not have_content('Natal')
    expect(page).to_not have_content('Promoção de Natal')
    expect(page).to_not have_content('10,00%')
  end
end
