Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

create a filter to tipo_estoque #411

Merged
merged 2 commits into from
Nov 18, 2024
Merged
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
168 changes: 154 additions & 14 deletions app/controllers/stocks_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,31 @@ def index
@stocks_with_data = collection
end
format.csv do
GenerateStocksCsvJob.perform_later(current_tenant, current_user.email)
flash[:notice] = "CSV is being generated and will be emailed to you shortly."
redirect_to stocks_path
if params[:detailed]
begin
csv_data = generate_detailed_csv
send_data csv_data,
filename: "detailed_stock_calculations_#{Date.today}.csv",
type: 'text/csv; charset=utf-8',
disposition: 'attachment'
rescue => e
Rails.logger.error "Error generating CSV: #{e.message}"
flash[:error] = "Erro ao gerar o CSV. Por favor, tente novamente."
redirect_to stocks_path
end
else
begin
csv_data = generate_regular_csv
send_data csv_data,
filename: "stock_calculations_#{Date.today}.csv",
type: 'text/csv; charset=utf-8',
disposition: 'attachment'
rescue => e
Rails.logger.error "Error generating CSV: #{e.message}"
flash[:error] = "Erro ao gerar o CSV. Por favor, tente novamente."
redirect_to stocks_path
end
end
end
end
end
Expand Down Expand Up @@ -66,13 +88,68 @@ def collection
@default_situation_balance_filter = params['balance_situation']
@default_sku_filter = params['sku']
@default_period_filter = params['period'] || '30'
@default_tipo_estoque = params['tipo_estoque'] || 'null'

stocks = Stock.where(account_id: current_tenant)
.includes(:product, :balances)
.only_positive_price(true)
.filter_by_status(params['status'])
.filter_by_total_balance_situation(params['balance_situation'])
.filter_by_sku(params['sku'])
.joins(:product)

# Calculate total balance for null tipo_estoque products
default_warehouse_id = '9023657532' # ID for Estoque São Paulo Base
null_tipo_stocks = Stock.where(account_id: current_tenant)
.joins(:product)
.includes(:product)
.where(products: { tipo_estoque: nil })
.includes(:balances)
.to_a

Rails.logger.info "Starting balance calculations..."

@total_null_balance = null_tipo_stocks.sum do |stock|
balance = stock.balances.find { |b| b.deposit_id.to_s == default_warehouse_id }
next 0 unless balance

# Get the base physical balance
physical_balance = balance.physical_balance

# Calculate the actual balance based on conditions
actual_balance = if stock.discounted_warehouse_sku_id == "#{default_warehouse_id}_#{stock.product.sku}"
# If discounted, subtract 1000 first
discounted = physical_balance - 1000
# If the discounted value is negative, don't count this stock
discounted <= 0 ? 0 : discounted
else
# If not discounted, apply the regular rules
if physical_balance >= 1000
physical_balance - 1000
elsif physical_balance <= 0
0
else
physical_balance
end
end

Rails.logger.info "SKU: #{stock.product.sku} | Physical Balance: #{physical_balance} | Actual Balance: #{actual_balance} | Discounted?: #{stock.discounted_warehouse_sku_id.present?}"

actual_balance
end

Rails.logger.info "Total balance calculated: #{@total_null_balance}"

stocks = case @default_tipo_estoque
when 'null'
stocks.where(products: { tipo_estoque: nil })
when 'V'
stocks.where(products: { tipo_estoque: 'V' })
when 'P'
stocks.where(products: { tipo_estoque: 'P' })
else
stocks
end

@warehouses = Warehouse.where(account_id: current_tenant).pluck(:bling_id, :description).to_h

Expand All @@ -86,8 +163,6 @@ def collection
.group(:sku)
.sum(:quantity)

default_warehouse_id = '9023657532' # ID for Estoque São Paulo Base

total_in_production = Stock.total_in_production_for_all

stocks_with_forecasts = stocks.map do |stock|
Expand All @@ -98,16 +173,20 @@ def collection
virtual_balance = default_balance ? default_balance.virtual_balance : 0
in_production = stock.total_in_production

# Calculate discounted balances
discounted_physical_balance = stock.discounted_balance(default_balance) if default_balance
discounted_virtual_balance = stock.discounted_virtual_balance(default_balance) if default_balance
# Calculate discounted balances if default_balance exists
discounted_physical_balance = default_balance ? stock.discounted_balance(default_balance) : 0
discounted_virtual_balance = default_balance ? stock.discounted_virtual_balance(default_balance) : 0

# Calculate adjusted balances
adjusted_physical_balance = stock.adjusted_balance(default_balance) if default_balance
adjusted_virtual_balance = stock.adjusted_virtual_balance(default_balance) if default_balance
# Calculate adjusted balances if default_balance exists
adjusted_physical_balance = default_balance ? stock.adjusted_balance(default_balance) : 0
adjusted_virtual_balance = default_balance ? stock.adjusted_virtual_balance(default_balance) : 0

# Calculate forecast: total_sold - adjusted_physical_balance
total_forecast = [total_sold - adjusted_physical_balance, 0].max if adjusted_physical_balance
# Calculate forecast only if we have adjusted_physical_balance
total_forecast = if adjusted_physical_balance && !stock.product.composed?
[total_sold - adjusted_physical_balance, 0].max
else
0
end

[stock, {
total_sold: total_sold,
Expand All @@ -123,7 +202,8 @@ def collection
}]
end

sorted_stocks = stocks_with_forecasts.sort_by { |_, data| -data[:total_sold] }
# Sort by total_sold, but handle nil values
sorted_stocks = stocks_with_forecasts.sort_by { |_, data| -(data[:total_sold] || 0) }

@pagy, @stocks_with_data = pagy_array(sorted_stocks, items: 20)

Expand All @@ -140,4 +220,64 @@ def set_stock
flash[:alert] = "Stock not found"
redirect_to stocks_path
end

def generate_detailed_csv
require 'csv'

default_warehouse_id = '9023657532'
stocks = Stock.where(account_id: current_tenant)
.includes(:product, :balances)
.joins(:product)
.where(products: { tipo_estoque: nil })

CSV.generate(headers: true, col_sep: ';', encoding: 'UTF-8') do |csv|
csv << [
'SKU', 'Saldo Físico'
]

stocks.each do |stock|
begin
balance = stock.balances.find { |b| b.deposit_id.to_s == default_warehouse_id }
next unless balance

physical_balance = if stock.discounted_warehouse_sku_id == "#{default_warehouse_id}_#{stock.product.sku}"
stock.discounted_balance(balance)
else
balance.physical_balance
end

csv << [
stock.product.sku,
physical_balance || 0
]
rescue => e
Rails.logger.error "Error processing stock #{stock.id}: #{e.message}"
next
end
end
end
end

def generate_regular_csv
CSV.generate(headers: true, col_sep: ';', encoding: 'UTF-8') do |csv|
csv << ['id', 'SKU', 'Saldo Total', 'Saldo Virtual Total', 'Quantidade Vendida dos Últimos 30 dias',
'Previsão para os Próximos 30 dias', 'Produto']

stocks = Stock.where(account_id: current_tenant)
.includes(:product, :balances)
.joins(:product)
.where(products: { tipo_estoque: nil })
.sort_by(&:calculate_basic_forecast)
.reverse!

stocks.each do |stock|
next if stock.total_balance.zero? && stock.count_sold.zero?

row = [stock.id, stock.sku, stock.total_balance, stock.total_virtual_balance, stock.count_sold,
stock.calculate_basic_forecast,
stock.product.name]
csv << row
end
end
end
end
6 changes: 3 additions & 3 deletions app/models/product.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,12 @@
# id :bigint not null, primary key
# active :boolean
# bar_code :string
# componentes :jsonb
# extra_sku :string
# highlight :boolean
# lancamento_estoque :string
# name :string
# number_of_pieces_per_fabric_roll :integer
# price :float
# sku :string
# tipo_estoque :string
# created_at :datetime not null
# updated_at :datetime not null
# account_id :integer
Expand Down Expand Up @@ -180,6 +177,9 @@ def component_quantities

# Check if product is composed of other products
def composed?
# Return false if tipo_estoque is nil
return false if tipo_estoque.nil?

tipo_estoque == 'V' && componentes.present?
end

Expand Down
53 changes: 42 additions & 11 deletions app/views/stocks/index.html.erb
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
<%- model_class = Stock -%>
<div class="section-header row-cols-2">
<div class="page-header">
<h1><%= t('stocks.two') %></h1>
</div>
<div class="d-flex justify-content-end">
<%= button_to "Gerar Previsão de Venda", stocks_path(format: "csv"), class: "btn btn-success", method: :get %>
<div class="section-header">
<div class="d-flex justify-content-between align-items-center w-100">
<div class="d-flex align-items-center">
<h1 class="me-3"><%= t('stocks.two') %></h1>
<div class="total-balance-counter">
<h4>Total de Estoque (Tipo Null): <span class="badge bg-primary"><%= number_with_delimiter(@total_null_balance) %></span></h4>
</div>
</div>
<div class="d-flex gap-2">
<%= button_to stocks_path(format: "csv", detailed: true),
class: "btn btn-info",
method: :get,
form: { target: '_blank' } do %>
<i class="fas fa-download"></i> Cálculo Detalhado
<% end %>
<%= button_to stocks_path(format: "csv"),
class: "btn btn-success",
method: :get do %>
Gerar Previsão de Venda
<% end %>
</div>
</div>
</div>

Expand All @@ -27,8 +42,19 @@
options_for_select([['30 dias', '30'], ['15 dias', '15'], ['7 dias', '7']], @default_period_filter),
class: "form-select" %>
</div>

<div class="col-md-2">
<%= select_tag "tipo_estoque",
options_for_select([
['Tipo Null', 'null'],
['Tipo V', 'V'],
['Tipo P', 'P'],
['Todos', 'all']
], @default_tipo_estoque),
class: "form-select" %>
</div>

<div class="col-md-3">
<div class="col-md-2">
<%= text_field_tag "sku", @default_sku_filter,
placeholder: "Filtrar por SKU",
class: "form-control" %>
Expand Down Expand Up @@ -65,7 +91,7 @@
<% sao_paulo_base = stock.balances.find { |b| b.deposit_id.to_s == '9023657532' } %>
<tr>
<td>
<% if sao_paulo_base %>
<% if sao_paulo_base && !stock.product.composed? %>
<%= check_box_tag "discount_#{stock.id}_#{sao_paulo_base.deposit_id}", 1,
stock.discounted_warehouse_sku_id == "#{sao_paulo_base.deposit_id}_#{stock.product.sku}",
class: 'discount-checkbox',
Expand All @@ -84,11 +110,16 @@
</td>
<td class="in-production"><%= data[:total_in_production] %></td>
<td><%= data[:total_sold] %></td>
<td class="forecast"><%= data[:total_forecast] %></td>
<td class="forecast"><%= stock.product.composed? ? 'N/A' : data[:total_forecast] %></td>
<td class="number-of-rolls">
<%= (data[:total_forecast].to_f / (stock.product.number_of_pieces_per_fabric_roll || 1)).ceil %>
<%= stock.product.composed? ? 'N/A' : (data[:total_forecast].to_f / (stock.product.number_of_pieces_per_fabric_roll || 1)).ceil %>
</td>
<td>
<%= stock.product.name %>
<% if stock.product.composed? %>
<span class="badge bg-info">Composto</span>
<% end %>
</td>
<td><%= stock.product.name %></td>
<td>
<%= link_to icon('fas fa-eye'), stock, title: t('show'), class: 'btn btn-info', data: { toggle: 'tooltip', turbo: false } %>
</td>
Expand Down
Loading