mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-01-30 19:52:20 +01:00
(feat) update the stock total
This commit is contained in:
parent
f705f71c4f
commit
feabded2a0
@ -18,8 +18,7 @@ class API::ProductsController < API::ApiController
|
|||||||
|
|
||||||
def create
|
def create
|
||||||
authorize Product
|
authorize Product
|
||||||
@product = Product.new(product_params)
|
@product = ProductService.create(product_params, params[:product][:product_stock_movements_attributes])
|
||||||
@product.amount = ProductService.amount_multiplied_by_hundred(@product.amount)
|
|
||||||
if @product.save
|
if @product.save
|
||||||
render status: :created
|
render status: :created
|
||||||
else
|
else
|
||||||
@ -30,9 +29,8 @@ class API::ProductsController < API::ApiController
|
|||||||
def update
|
def update
|
||||||
authorize @product
|
authorize @product
|
||||||
|
|
||||||
product_parameters = product_params
|
@product = ProductService.update(@product, product_params, params[:product][:product_stock_movements_attributes])
|
||||||
product_parameters[:amount] = ProductService.amount_multiplied_by_hundred(product_parameters[:amount])
|
if @product.save
|
||||||
if @product.update(product_parameters)
|
|
||||||
render status: :ok
|
render status: :ok
|
||||||
else
|
else
|
||||||
render json: @product.errors.full_messages, status: :unprocessable_entity
|
render json: @product.errors.full_messages, status: :unprocessable_entity
|
||||||
@ -62,7 +60,6 @@ class API::ProductsController < API::ApiController
|
|||||||
:low_stock_alert, :low_stock_threshold,
|
:low_stock_alert, :low_stock_threshold,
|
||||||
machine_ids: [],
|
machine_ids: [],
|
||||||
product_files_attributes: %i[id attachment _destroy],
|
product_files_attributes: %i[id attachment _destroy],
|
||||||
product_images_attributes: %i[id attachment is_main _destroy],
|
product_images_attributes: %i[id attachment is_main _destroy])
|
||||||
product_stock_movements_attributes: %i[id quantity reason stock_type])
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -38,12 +38,13 @@ export default class ProductLib {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the given quantity, prefixed by its addition operator (- or +)
|
* Return the given quantity, prefixed by its addition operator (- or +), if needed
|
||||||
*/
|
*/
|
||||||
static absoluteStockMovement = (quantity: number, reason: StockMovementReason): string => {
|
static absoluteStockMovement = (quantity: number, reason: StockMovementReason): string => {
|
||||||
if (ProductLib.stockMovementType(reason) === 'in') {
|
if (ProductLib.stockMovementType(reason) === 'in') {
|
||||||
return `+${quantity}`;
|
return `+${quantity}`;
|
||||||
} else {
|
} else {
|
||||||
|
if (quantity < 0) return quantity.toString();
|
||||||
return `-${quantity}`;
|
return `-${quantity}`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -7,8 +7,8 @@ export interface ProductIndexFilter extends ApiFilter {
|
|||||||
|
|
||||||
export type StockType = 'internal' | 'external' | 'all';
|
export type StockType = 'internal' | 'external' | 'all';
|
||||||
|
|
||||||
export const stockMovementInReasons = ['inward_stock', 'returned', 'cancelled', 'inventory_fix'] as const;
|
export const stockMovementInReasons = ['inward_stock', 'returned', 'cancelled', 'inventory_fix', 'other_in'] as const;
|
||||||
export const stockMovementOutReasons = ['sold', 'missing', 'damaged'] as const;
|
export const stockMovementOutReasons = ['sold', 'missing', 'damaged', 'other_out'] as const;
|
||||||
export const stockMovementAllReasons = [...stockMovementInReasons, ...stockMovementOutReasons] as const;
|
export const stockMovementAllReasons = [...stockMovementInReasons, ...stockMovementOutReasons] as const;
|
||||||
|
|
||||||
export type StockMovementReason = typeof stockMovementAllReasons[number];
|
export type StockMovementReason = typeof stockMovementAllReasons[number];
|
||||||
|
@ -8,7 +8,9 @@ class ProductStockMovement < ApplicationRecord
|
|||||||
ALL_STOCK_TYPES = %w[internal external].freeze
|
ALL_STOCK_TYPES = %w[internal external].freeze
|
||||||
enum stock_type: ALL_STOCK_TYPES.zip(ALL_STOCK_TYPES).to_h
|
enum stock_type: ALL_STOCK_TYPES.zip(ALL_STOCK_TYPES).to_h
|
||||||
|
|
||||||
ALL_REASONS = %w[inward_stock returned cancelled inventory_fix sold missing damaged].freeze
|
INCOMING_REASONS = %w[inward_stock returned cancelled inventory_fix other_in].freeze
|
||||||
|
OUTGOING_REASONS = %w[sold missing damaged other_out].freeze
|
||||||
|
ALL_REASONS = [].concat(INCOMING_REASONS).concat(OUTGOING_REASONS).freeze
|
||||||
enum reason: ALL_REASONS.zip(ALL_REASONS).to_h
|
enum reason: ALL_REASONS.zip(ALL_REASONS).to_h
|
||||||
|
|
||||||
validates :stock_type, presence: true
|
validates :stock_type, presence: true
|
||||||
@ -16,10 +18,4 @@ class ProductStockMovement < ApplicationRecord
|
|||||||
|
|
||||||
validates :reason, presence: true
|
validates :reason, presence: true
|
||||||
validates :reason, inclusion: { in: ALL_REASONS }
|
validates :reason, inclusion: { in: ALL_REASONS }
|
||||||
|
|
||||||
before_create :set_date
|
|
||||||
|
|
||||||
def set_date
|
|
||||||
self.date = DateTime.current
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -33,7 +33,8 @@ module Payments::PaymentConcern
|
|||||||
order.payment_gateway_object = PaymentGatewayObject.new(gateway_object_id: payment_id, gateway_object_type: payment_type)
|
order.payment_gateway_object = PaymentGatewayObject.new(gateway_object_id: payment_id, gateway_object_type: payment_type)
|
||||||
end
|
end
|
||||||
order.order_items.each do |item|
|
order.order_items.each do |item|
|
||||||
ProductService.update_stock(item.orderable, 'external', 'sold', -item.quantity, item.id)
|
ProductService.update_stock(item.orderable,
|
||||||
|
[{ stock_type: 'external', reason: 'sold', quantity: item.quantity, order_item_id: item.id }]).save
|
||||||
end
|
end
|
||||||
order.save
|
order.save
|
||||||
order.reload
|
order.reload
|
||||||
|
@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
# Provides methods for Product
|
# Provides methods for Product
|
||||||
class ProductService
|
class ProductService
|
||||||
def self.list(filters)
|
class << self
|
||||||
|
def list(filters)
|
||||||
products = Product.includes(:product_images)
|
products = Product.includes(:product_images)
|
||||||
if filters[:is_active].present?
|
if filters[:is_active].present?
|
||||||
state = filters[:disabled] == 'false' ? [nil, false] : true
|
state = filters[:disabled] == 'false' ? [nil, false] : true
|
||||||
@ -12,7 +13,7 @@ class ProductService
|
|||||||
end
|
end
|
||||||
|
|
||||||
# amount params multiplied by hundred
|
# amount params multiplied by hundred
|
||||||
def self.amount_multiplied_by_hundred(amount)
|
def amount_multiplied_by_hundred(amount)
|
||||||
if amount.present?
|
if amount.present?
|
||||||
v = amount.to_f
|
v = amount.to_f
|
||||||
|
|
||||||
@ -23,12 +24,32 @@ class ProductService
|
|||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.update_stock(product, stock_type, reason, quantity, order_item_id = nil)
|
# @param product Product
|
||||||
remaining_stock = product.stock[stock_type] + quantity
|
# @param stock_movements [{stock_type: string, reason: string, quantity: number|string, order_item_id: number|nil}]
|
||||||
product.product_stock_movements.create(stock_type: stock_type, reason: reason, quantity: quantity, remaining_stock: remaining_stock,
|
def update_stock(product, stock_movements = nil)
|
||||||
date: DateTime.current,
|
remaining_stock = { internal: product.stock['internal'], external: product.stock['external'] }
|
||||||
order_item_id: order_item_id)
|
product.product_stock_movements_attributes = stock_movements&.map do |movement|
|
||||||
product.stock[stock_type] = remaining_stock
|
quantity = ProductStockMovement::OUTGOING_REASONS.include?(movement[:reason]) ? -movement[:quantity].to_i : movement[:quantity].to_i
|
||||||
product.save
|
remaining_stock[movement[:stock_type].to_sym] += quantity
|
||||||
|
{
|
||||||
|
stock_type: movement[:stock_type], reason: movement[:reason], quantity: quantity,
|
||||||
|
remaining_stock: remaining_stock[movement[:stock_type].to_sym], date: DateTime.current, order_item_id: movement[:order_item_id]
|
||||||
|
}
|
||||||
|
end || {}
|
||||||
|
product.stock = remaining_stock
|
||||||
|
product
|
||||||
|
end
|
||||||
|
|
||||||
|
def create(product_params, stock_movement_params = [])
|
||||||
|
product = Product.new(product_params)
|
||||||
|
update(product, product_params, stock_movement_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update(product, product_params, stock_movement_params = [])
|
||||||
|
product_params[:amount] = amount_multiplied_by_hundred(product_params[:amount])
|
||||||
|
product.attributes = product_params
|
||||||
|
update_stock(product, stock_movement_params)
|
||||||
|
product
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -2030,6 +2030,8 @@ en:
|
|||||||
sold: "Sold"
|
sold: "Sold"
|
||||||
missing: "Missing in stock"
|
missing: "Missing in stock"
|
||||||
damaged: "Damaged product"
|
damaged: "Damaged product"
|
||||||
|
other_in: "Other"
|
||||||
|
other_out: "Other"
|
||||||
orders:
|
orders:
|
||||||
heading: "Orders"
|
heading: "Orders"
|
||||||
create_order: "Create an order"
|
create_order: "Create an order"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user