Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 75 additions & 3 deletions app/controllers/admin/billing_profiles_controller.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
require 'countries'

module Admin
class BillingProfilesController < BaseController
before_action :authorize_user
before_action :set_billing_profile, only: %i[show edit update destroy]

# GET /admin/billing_profiles
def index
sort_column = params[:sort].presence_in(%w[users.surname name vat_code]) || 'users.surname'
sort_column = params[:sort].presence_in(%w[users.surname name vat_code]) || 'users.surname'
sort_direction = params[:direction].presence_in(%w[asc desc]) || 'desc'

billing_profiles = BillingProfile.accessible_by(current_ability)
Expand All @@ -16,12 +19,81 @@ def index
end

# GET /admin/billing_profiles/12
def show
@billing_profile = BillingProfile.accessible_by(current_ability).find(params[:id])
def show; end

# GET /admin/billing_profiles/new
def new
@billing_profile = BillingProfile.new
end

# GET /admin/billing_profiles/12/edit
def edit; end

# POST /admin/billing_profiles
def create
@billing_profile = BillingProfile.new(billing_profile_params)

respond_to do |format|
if @billing_profile.save
format.html do
redirect_to admin_billing_profile_path(@billing_profile), notice: t('billing_profiles.created')
end
format.json { render :show, status: :created, location: admin_billing_profile_path(@billing_profile) }
else
format.html { render :new }
format.json { render json: @billing_profile.errors, status: :unprocessable_entity }
end
end
end

# PUT /admin/billing_profiles/12
def update
respond_to do |format|
if @billing_profile.update(update_params)
format.html do
redirect_to admin_billing_profile_path(@billing_profile), notice: t('billing_profiles.updated')
end
format.json { render :show, status: :ok, location: admin_billing_profile_path(@billing_profile) }
else
format.html { render :edit }
format.json { render json: @billing_profile.errors, status: :unprocessable_entity }
end
end
end

# DELETE /admin/billing_profiles/12
def destroy
if @billing_profile.deletable?
@billing_profile.destroy
respond_to do |format|
format.html { redirect_to admin_billing_profiles_path, notice: t('billing_profiles.deleted') }
format.json { head :no_content }
end
else
respond_to do |format|
format.html { redirect_to admin_billing_profile_path(@billing_profile), alert: t('billing_profiles.in_use_by_offer') }
format.json { render json: @billing_profile.errors, status: :unprocessable_entity }
end
end
end

private

def set_billing_profile
@billing_profile = BillingProfile.accessible_by(current_ability).find(params[:id])
end

def billing_profile_params
params.require(:billing_profile)
.permit(:name, :vat_code, :street, :city, :postal_code, :country_code, :user_id)
end

def update_params
update_params = params.require(:billing_profile)
.permit(:name, :vat_code, :street, :city, :postal_code, :country_code)
merge_updated_by(update_params)
end

def authorize_user
authorize! :manage, BillingProfile
end
Expand Down
24 changes: 21 additions & 3 deletions app/controllers/admin/invoices_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
module Admin
class InvoicesController < BaseController
before_action :authorize_user
before_action :create_invoice_if_needed, except: :toggle_partial_payments
before_action :set_invoice, only: %i[show download update edit toggle_partial_payments]
before_action :authorize_for_update, only: %i[edit update]
before_action :create_invoice_if_needed, except: %i[toggle_partial_payments update_billing_profile]
before_action :set_invoice, only: %i[show download update edit toggle_partial_payments update_billing_profile]
before_action :authorize_for_update, only: %i[edit update update_billing_profile]

# GET /admin/invoices/aa450f1a-45e2-4f22-b2c3-f5f46b5f906b
def show
@payment_orders = @invoice.payment_orders
@billing_profiles = @invoice.user&.billing_profiles || []
end

# GET /admin/invoices
Expand Down Expand Up @@ -83,6 +84,23 @@ def toggle_partial_payments
end
end

# PATCH /admin/invoices/aa450f1a-45e2-4f22-b2c3-f5f46b5f906b/update_billing_profile
def update_billing_profile
respond_to do |format|
if @invoice.update(billing_profile_id: params[:invoice][:billing_profile_id])
format.html do
redirect_to admin_invoice_path(@invoice), notice: t('invoices.billing_profile_updated')
end
format.json { render :show, status: :ok, location: @invoice }
else
format.html do
redirect_to admin_invoice_path(@invoice), alert: @invoice.errors.full_messages.join(', ')
end
format.json { render json: @invoice.errors, status: :unprocessable_entity }
end
end
end

private

def set_invoice
Expand Down
5 changes: 5 additions & 0 deletions app/models/billing_profile.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ def vat_code_must_be_registered_in_vies
return if vat_code.blank? || vat_rate == BigDecimal(0)

errors.add(:vat_code, I18n.t('billing_profiles.vat_validation_error')) unless Valvat.new(vat_code).exists?
rescue Valvat::ServiceUnavailable
errors.add(:vat_code, I18n.t('billing_profiles.vat_validation_service_unavailable_error'))
rescue Valvat::MemberStateUnavailable
errors.add(:vat_code, I18n.t('billing_profiles.vat_validation_member_state_unavailable_error'))
true
rescue Valvat::RateLimitError
errors.add(:vat_code, I18n.t('billing_profiles.vat_validation_rate_limit_error'))
end
Expand Down
60 changes: 60 additions & 0 deletions app/views/admin/billing_profiles/_form.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<%= form_with model: billing_profile, url: url, id: 'billing_profile_form' do |f| %>
<div style='display: flex; align-items: center; gap: 0 20px; margin-bottom: 20px;'>
<%= f.label :name, t('billing_profiles.name'), style: 'width: 150px;' %>
<%= f.text_field :name, class: "form-control", autofocus: true, autocomplete: "off" %>
</div>

<div style='display: flex; align-items: center; gap: 0 20px; margin-bottom: 20px;'>
<%= f.label :vat_code, t('billing_profiles.vat_code'), style: 'width: 150px;' %>
<%= f.text_field :vat_code, class: "form-control", autocomplete: "off" %>
</div>

<div style='display: flex; align-items: center; gap: 0 20px; margin-bottom: 20px;'>
<%= f.label :street, t('billing_profiles.street'), style: 'width: 150px;' %>
<%= f.text_field :street, class: "form-control", autocomplete: "off" %>
</div>

<div style='display: flex; align-items: center; gap: 0 20px; margin-bottom: 20px;'>
<%= f.label :city, t('billing_profiles.city'), style: 'width: 150px;' %>
<%= f.text_field :city, class: "form-control", autocomplete: "off" %>
</div>

<div style='display: flex; align-items: center; gap: 0 20px; margin-bottom: 20px;'>
<%= f.label :postal_code, t('billing_profiles.postal_code'), style: 'width: 150px;' %>
<%= f.text_field :postal_code, class: "form-control", autocomplete: "off" %>
</div>

<div style='display: flex; align-items: center; gap: 0 20px; margin-bottom: 20px;'>
<%= f.label :country_code, t('billing_profiles.country'), style: 'width: 150px;' %>
<%= f.select :country_code,
options_for_select(
Countries.for_selection,
billing_profile.country_code || Setting.find_by(code: 'default_country').retrieve
),
{},
class: "ui dropdown" %>
</div>

<div style='display: flex; align-items: center; gap: 0 20px; margin-bottom: 20px;'>
<%= f.label :user_id, t('billing_profiles.user'), style: 'width: 150px;' %>
<% if billing_profile.new_record? %>
<%= f.select :user_id,
options_from_collection_for_select(User.order(:surname), :id, :display_name, billing_profile.user_id),
{ include_blank: t('.select_user') },
class: "ui dropdown" %>
<% elsif billing_profile.user.present? %>
<%= link_to billing_profile.user.display_name, admin_user_path(billing_profile.user) %>
<% else %>
<span><%= t('billing_profiles.orphaned') %></span>
<% end %>
</div>

<br>
<div style='padding: 1rem 0; display: flex; justify-content: end; gap: 10px;'>
<%= f.button t(:submit), class: "c-btn c-btn--blue", data: { turbo: false } %>
</div>

<div class="c-table__filters__btns" style='padding: 1rem 0; display: flex; justify-content: start;'>
<%= link_to t(:back), admin_billing_profiles_path, class: "c-btn c-btn--green" %>
</div>
<% end %>
18 changes: 18 additions & 0 deletions app/views/admin/billing_profiles/edit.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<% content_for :title, t('.title') %>

<div class="o-container">
<% if @billing_profile.errors.any? %>
<div class="ui message">
<div class="header">
<%= t(:errors_name) %>
</div>
<ul id="errors">
<% @billing_profile.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>

<%= render 'form', billing_profile: @billing_profile, url: admin_billing_profile_path(@billing_profile) %>
</div>
9 changes: 8 additions & 1 deletion app/views/admin/billing_profiles/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<%= form_with url: admin_billing_profiles_path, method: :get do |f| %>
<div class="c-table__filters" >
<div class="c-table__filters__btns" style='display: flex !important; align-items: center !important; justify-content: space-between !important; width: 100%;'>
<%= f.search_field :search_string, value: params[:search_string], placeholder: t('search_by_domain_name'), class: 'c-table__search__input js-table-search-dt' %>
<%= f.search_field :search_string, value: params[:search_string], placeholder: 'Search by name', class: 'c-table__search__input js-table-search-dt' %>
<%= f.button t('.search'), class: "c-btn c-btn--blue" %>
</div>
</div>
Expand Down Expand Up @@ -36,5 +36,12 @@
<% end %>
<% end %>
<% end %>

<%= component 'common/pagy', pagy: @pagy %>

<div class="c-table__filters__btns" style='padding: 1rem 0; display: flex; justify-content: end;'>
<%= link_to t('.new_billing_profile'), new_admin_billing_profile_path, class: "c-btn c-btn--green" %>
</div>
</div>
</div>

18 changes: 18 additions & 0 deletions app/views/admin/billing_profiles/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<% content_for :title, t('.title') %>

<div class="o-container">
<% if @billing_profile.errors.any? %>
<div class="ui message">
<div class="header">
<%= t(:errors_name) %>
</div>
<ul id="errors">
<% @billing_profile.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>

<%= render 'form', billing_profile: @billing_profile, url: admin_billing_profiles_path %>
</div>
25 changes: 21 additions & 4 deletions app/views/admin/billing_profiles/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@

<%= component 'common/table', header_collection: [], options: { class: 'js-table-dt dataTable no-footer' } do %>
<%= tag.tbody class: 'contents' do %>
<tr>
<td><strong><%= t('billing_profiles.name') %></strong></td>
<td><%= @billing_profile.name %></td>
</tr>

<tr>
<td><strong><%= t('billing_profiles.vat_code') %></strong></td>
<td><%= @billing_profile.vat_code %></td>
Expand All @@ -32,13 +37,25 @@
<td><strong><%= t('billing_profiles.country') %></strong></td>
<td><%= @billing_profile.country_code %></td>
</tr>

<tr>
<td><strong><%= t('billing_profiles.user') %></strong></td>
<td>
<% if @billing_profile.user %>
<%= link_to @billing_profile.user.display_name, admin_user_path(@billing_profile.user) %>
<% else %>
<%= t('billing_profiles.orphaned') %>
<% end %>
</td>
</tr>
<% end %>
<% end %>

<div class="c-table__filters__btns" style='padding: 1rem 0; display: flex; justify-content: end;'>
<%- if @billing_profile.user %>
<%= link_to t(:user), admin_user_path(@billing_profile.user), class: "c-btn c-btn--blue" %>
<% end %>
<div class="c-table__filters__btns" style='padding: 1rem 0; display: flex; justify-content: end; gap: 10px;'>
<%= link_to t(:edit), edit_admin_billing_profile_path(@billing_profile), class: "c-btn c-btn--orange" %>
<%= button_to t(:delete), admin_billing_profile_path(@billing_profile), method: :delete, class: "c-btn c-btn--red", form: { data: { turbo_confirm: t(".confirm_delete") } } %>
<%= link_to t(:versions_name), admin_billing_profile_versions_path(@billing_profile), class: "c-btn c-btn--blue" %>
<%= link_to t(:back), admin_billing_profiles_path, class: "c-btn c-btn--gray" %>
</div>
</div>
</div>
Loading
Loading