class PriceLineBlock < ApplicationRecord belongs_to :price_lineable, :polymorphic => true #validates :wish_date, :presence => true, :if => :wish_date_needed? has_many :price_lines accepts_nested_attributes_for :price_lines, allow_destroy: true belongs_to :p_devise validates :p_customer_id, :presence => true, :if => :p_customer_needed? #validates :p_fournisseur_id, :presence => true, :if => :p_fournisseur_needed? #validates :particular_bill_id, :presence => true, :if => :particular_bill_needed? #validates :particular_send_id, :presence => true, :if => :particular_send_needed? belongs_to :particular_bill, :class_name => "Particular"#, :dependent => :destroy accepts_nested_attributes_for :particular_bill belongs_to :particular_send, :class_name => "Particular"#, :dependent => :destroy accepts_nested_attributes_for :particular_send has_many :line_blocks belongs_to :p_customer belongs_to :p_payment_type attr_accessor :same_adress PURCHASE_BLOCKS = ["Réponse fournisseur", "Consultation fournisseur", "Facture d'achat", "Commande achat", "Demande prix", "Bon de commande achat", "Bon de réception achat", "Facture achat", "Avoir achat", "Catalogue fournisseur"] SALE_BLOCKS = ["Demande de commande", "Bon de commande", "Devis", "Bon de livraison", "Facture", "Avoir"] belongs_to :p_fournisseur accepts_nested_attributes_for :p_fournisseur belongs_to :price_document def to_no_archive if self.imported %w() else %w(cost_ht marge_ht) end end def ca_cost_ht self.price_lines.sum(:cc_cost_ht) end def ca_marge_ht self.tot_amount_ht - self.ca_cost_ht end def test_if_stock_ok? to_r = true self.price_lines.each do |pl| if pl.p_product_ref && pl.p_product_ref.stocked if pl.p_product_ref.assembled pl.p_product_ref.p_product_assembleds.all.each do |ppa| if LineStock.where(:p_product_ref_id => ppa.p_product_ref_id).sum(:qte_available) >= (pl.qte.to_f * ppa.qte.to_f) else to_r = false end end else if LineStock.where(:p_product_ref_id => pl.p_product_ref_id).sum(:qte_available) >= pl.qte else to_r = false end end end end return to_r end def test_if_price_stock_ok? to_r = true self.price_lines.each do |pl| if pl.p_product_ref && pl.p_product_ref.stocked puts pl.p_product_ref.p_product_id if pl.p_product_ref.p_product and pl.p_product_ref.p_product.ct_purchase_price_ht.to_f > 0.0 else to_r = false end end end return to_r end def update_stocks_from_average if self.block_type == "Facture" and !self.cost_ok if self.test_if_price_stock_ok? self.price_lines.each do |pl| if pl.p_product_ref pl.ct_cost_ht = pl.p_product_ref.p_product.ct_purchase_price_ht.to_f * pl.qte.to_f pl.save end end self.cost_ok = true self.save self.price_lineable.save if self.price_lineable end end end def update_stocks_for(p_product_ref, qte, pl) if !pl.p_articles.empty? pl.p_articles.each do |p_article| decr_line_stock = LineStock.new(:date => self.price_lineable.date, :p_product_ref => p_product_ref, :qte => -1, :description => "Attribution à la ligne d'une facture", :price_ht => 0.0, :price_line => pl, :price_line_block => self, :stockable => self.price_lineable) price = 0.0 line_stock_usages = [] #qte_to_affect = qte lsu_p_art = LineStockUsage.where(p_article_id: p_article.id) line_stocks_p_article = p_article.line_stocks.where("qte_available > ?", 0) #LineStock.joins(:p_articles).where("p_articles.id = ?", p_article.id) ls = nil line_stocks_p_article.each do |p_article_line_stock| if p_article_line_stock.line_stock_usages.where(p_article_id: p_article).empty? ls = p_article_line_stock end end #ls = LineStock.joins(:p_articles).where("p_articles.id = ?", p_article.id).joins(:line_stock_usages) #ls.qte_available = ls.qte_available - 1 #qte_to_affect = qte_to_affect - qte_here lsu = LineStockUsage.create(:qte => 1, :line_stock => ls, :dest_line_stock_id => decr_line_stock.id, p_article_id: p_article.id) #line_stock_usages << lsu price += lsu.price_ht ls.save decr_line_stock.price_ht = price * -1 decr_line_stock.save ls_p_art = LineStockPArticle.new( p_article: p_article, line_stock: decr_line_stock ) ls_p_art.save #p_article.line_stocks << decr_line_stock fonctionne aussi selon la doc : https://guides.rubyonrails.org/association_basics.html#:~:text=The%20collection%20of%20join%20models%20can%20be%20managed end else #=================================================================================================================== puts "TEST" puts "Référence : #{p_product_ref.id} #{p_product_ref.p_product.name}" if qte.to_f != 0.0 decr_line_stock = LineStock.create(:date => self.price_lineable.date, :p_product_ref => p_product_ref, :qte => qte*-1, :description => "Attribution à la ligne d'une facture", :price_ht => 0.0, :price_line => pl, :price_line_block => self, :stockable => self.price_lineable) price = 0.0 line_stock_usages = [] qte_to_affect = qte #LineStock.where(:p_product_ref_id => p_product_ref.id).where("date <= ?", self.price_lineable.date).where("qte_available > 0.0").each do |ls| LineStock.where(:p_product_ref_id => p_product_ref.id).where("date <= ?", self.price_lineable.date).where("qte_available > 0.0").each do |ls| if qte_to_affect <= ls.qte_available qte_here = qte_to_affect else qte_here = ls.qte_available end qte_to_affect = qte_to_affect - qte_here lsu = LineStockUsage.create(:qte => qte_here, :line_stock => ls, :dest_line_stock_id => decr_line_stock.id) line_stock_usages << lsu price += lsu.price_ht ls.save break if qte_to_affect == 0.0 #price_u_ht end decr_line_stock.p_articles = pl.p_articles decr_line_stock.price_ht = price * -1 decr_line_stock.save end #=================================================================================================================== end end def update_stocks if self.block_type == "Facture" or self.block_type == "Bon de commande client" or self.block_type == "Bon de réception achat" && !self.cost_ok if self.test_if_stock_ok? self.price_lines.each do |pl| if pl.p_product_ref && pl.p_product_ref.stocked if pl.p_product_ref.assembled pl.p_product_ref.p_product_assembleds.all.each do |ppa| self.update_stocks_for(ppa.p_product_ref, (pl.qte.to_f * ppa.qte.to_f), pl) end else self.update_stocks_for(pl.p_product_ref, pl.qte, pl) end end end self.cost_ok = true self.save self.save # deuxième sauvegarde pour prendre en compte les couts de price_line self.price_lineable.save if self.price_lineable return true end end end def generate_stock self.price_lines.each do |pl| if pl.p_product_ref ls = LineStock.new( :dluo => pl.dluo, :date => self.price_lineable.date, :p_product_ref => pl.p_product_ref, :description => "Entrée en stock par facture d'achat", :qte => pl.qte, :price_ht => pl.local_tot_amount_ht, :price_line => pl, :price_line_block => self, :stockable => self.price_lineable) ls.p_articles = pl.p_articles ls.save end pl.p_articles.joins(:p_grade).where(p_grades: {grade: "RMA"}).each do |p_article| decr_line_stock = LineStock.new( :date => self.price_lineable.date, :p_product_ref => pl.p_product_ref, :qte => -1, :description => "Sortie de stock automatique des RMA", :price_ht => 0.0, :price_line => pl, :price_line_block => self, :stockable => self.price_lineable) decr_line_stock.p_articles << p_article decr_line_stock.save end end self.stock_ok = true self.save self.price_lineable.save if self.price_lineable end def purchase? if PriceLineBlock::PURCHASE_BLOCKS.include?(self.block_type) true else false end end def sale? if PriceLineBlock::SALE_BLOCKS.include?(self.block_type) true else false end end def devise_symbol if self.p_devise self.p_devise.symbol else "€" end end def ca_devise_rate if self.local_amount_ttc if self.tot_amount_ttc != 0.0 self.local_amount_ttc / self.tot_amount_ttc else 0.0 end elsif self.p_devise return self.p_devise.rate.to_f else 1.0 end end def wish_date_needed? if PriceLineBlock::PURCHASE_BLOCKS.include?(self.block_type) false elsif self.block_type == "Devis" or self.block_type == "Demande de commande" false else raise true end end def particular_bill_needed? if !self.imported if PriceLineBlock::PURCHASE_BLOCKS.include?(self.block_type) false elsif self.block_type == "Devis" false else true end end end def particular_send_needed? if !self.imported if PriceLineBlock::PURCHASE_BLOCKS.include?(self.block_type) false elsif self.block_type == "Devis" false else true end end end def p_fournisseur_needed? if ["Demande prix", "Consultation fournisseur", "Réponse fournisseur"].include?(self.block_type) false elsif PriceLineBlock::PURCHASE_BLOCKS.include?(self.block_type) true else false end end def p_customer_needed? if PriceLineBlock::PURCHASE_BLOCKS.include?(self.block_type) false elsif self.block_type == "Devis" false else true end end acts_as_caching :fields => [:state, :solded_nbr_days, :solded_at, :cost_ht, :marge_ht, :devise_rate, :bon_de_commande_id, :bon_de_livraison_id, :facture_id, :reliquat, :customer_market_discount_percent, :block_type, :discount_comptant, :fdp_tva_rate, :weight_tot, :accounting_zone_id, :accounting_zone_name, :tot_lines_ht, :tot_lines_tva, :tot_lines_ttc, :tot_fdp_ht, :tot_fdp_tva, :tot_fdp_ttc, :tot_discount_ht, :tot_discount_tva, :tot_discount_ttc, :tot_amount_af_discount_ht, :tot_amount_af_discount_tva, :tot_amount_af_discount_ttc, :gen_discount_percent, :tot_gen_discount_ht, :tot_gen_discount_tva, :tot_gen_discount_ttc, :tot_amount_ht, :tot_amount_tva, :tot_amount_ttc, :remise_enrobage_ok, :remise_ecole_ok, :remise_pre_order_ok, :remise_qte_ok, :payment_comptant, :payment_delais, :payment_month_end, :payment_end_at, :acompte, :acompte_percent, :payment_days, :nbr_ship, :creation_date, :p_customer_cat_id] def ca_state self.price_lineable.state rescue nil end BON_DE_COMMANDE_TO_RESET = %w(weight_tot tot_lines_ht tot_lines_tva tot_lines_ttc tot_fdp_ht tot_fdp_tva tot_fdp_ttc tot_discount_ht tot_discount_tva tot_discount_ttc tot_amount_af_discount_ht tot_amount_af_discount_tva tot_amount_af_discount_ttc gen_discount_percent tot_gen_discount_ht tot_gen_discount_tva tot_gen_discount_ttc tot_amount_ht tot_amount_tva tot_amount_ttc nbr_ship) BON_DE_LIVRAISON_TO_RESET = %w(weight_tot tot_lines_ht tot_lines_tva tot_lines_ttc tot_fdp_ht tot_fdp_tva tot_fdp_ttc tot_discount_ht tot_discount_tva tot_discount_ttc tot_amount_af_discount_ht tot_amount_af_discount_tva tot_amount_af_discount_ttc gen_discount_percent tot_gen_discount_ht tot_gen_discount_tva tot_gen_discount_ttc tot_amount_ht tot_amount_tva tot_amount_ttc nbr_ship) FACTURE_ACHAT_TO_RESET = %w(devise_rate weight_tot tot_lines_ht tot_lines_tva tot_lines_ttc tot_fdp_ht tot_fdp_tva tot_fdp_ttc tot_discount_ht tot_discount_tva tot_discount_ttc tot_amount_af_discount_ht tot_amount_af_discount_tva tot_amount_af_discount_ttc gen_discount_percent tot_gen_discount_ht tot_gen_discount_tva tot_gen_discount_ttc tot_amount_ht tot_amount_tva tot_amount_ttc nbr_ship) BON_DE_RECEPTION_ACHAT_TO_RESET = %w(devise_rate weight_tot tot_lines_ht tot_lines_tva tot_lines_ttc tot_fdp_ht tot_fdp_tva tot_fdp_ttc tot_discount_ht tot_discount_tva tot_discount_ttc tot_amount_af_discount_ht tot_amount_af_discount_tva tot_amount_af_discount_ttc gen_discount_percent tot_gen_discount_ht tot_gen_discount_tva tot_gen_discount_ttc tot_amount_ht tot_amount_tva tot_amount_ttc nbr_ship) COMMANDE_ACHAT_TO_RESET = %w(weight_tot tot_lines_ht tot_lines_tva tot_lines_ttc tot_fdp_ht tot_fdp_tva tot_fdp_ttc tot_discount_ht tot_discount_tva tot_discount_ttc tot_amount_af_discount_ht tot_amount_af_discount_tva tot_amount_af_discount_ttc gen_discount_percent tot_gen_discount_ht tot_gen_discount_tva tot_gen_discount_ttc tot_amount_ht tot_amount_tva tot_amount_ttc nbr_ship) def reset_for_update self.price_lines.each do |pl| pl.reset_for_update end self.price_lines.each do |pl| pl.bk_price_ht = pl.default_price_u_ht if !pl.bk_price_ht pl.save end eval("#{self.block_type_slug.upcase}_TO_RESET").each do |qid| eval("self.ac_#{qid} = nil") end self.archive_now() self.price_lines.each do |pl| pl.archive_now() end end def ca_solded_at if self.price_lineable and self.price_lineable_type == "PriceDocument" self.price_lineable.solded_at end end def ca_solded_nbr_days if self.price_lineable and self.price_lineable_type == "PriceDocument" self.price_lineable.solded_nbr_days end end def ca_customer_market_discount_percent self.p_customer.market_discount.percent if self.p_customer and self.p_customer.market_discount end def personalised_archive self.price_lines.each do |pl| pl.archive_now end end def personalised_unarchive self.price_lines.each do |pl| pl.unarchive_now end end def ca_bon_de_commande_id self.price_lineable.bon_de_commande_id if self.price_lineable_type == "PriceDocument" and self.price_lineable end def ca_bon_de_livraison_id self.price_lineable.bon_de_livraison_id if self.price_lineable_type == "PriceDocument" and self.price_lineable end def ca_facture_id self.price_lineable.facture_id if self.price_lineable_type == "PriceDocument" and self.price_lineable end def ca_reliquat self.price_lineable.reliquat if self.price_lineable_type == "PriceDocument" and self.price_lineable end def ca_block_type self.price_lineable.block_type if self.price_lineable end def block_type_slug self.block_type.to_slug.gsub("-", "_") end before_validation do self.cancelled = self.price_lineable.cancelled if self.price_lineable and self.price_lineable_type == "PriceDocument" if !self.id and self.p_customer self.p_commercial_id = self.p_customer.p_commercial_id self.p_payment_type_id = self.p_customer.p_payment_type_id self.ct_payment_comptant = self.p_customer.comptant self.ct_acompte = self.p_customer.acompte self.ct_acompte_percent = self.p_customer.acompte_percent self.ct_payment_delais = self.p_customer.payment_delais self.ct_payment_month_end = self.p_customer.payment_fin_de_mois end if self.particular_bill_needed? and (!self.p_customer or !self.particular_bill or !self.particular_bill.owner or self.particular_bill.owner != self.p_customer) errors.add(:particular_bill_id, 'doit être une adresse du client') end if self.particular_send_needed? and (!self.p_customer or !self.particular_send or !self.particular_send.owner or self.particular_send.owner != self.p_customer) errors.add(:particular_send_id, 'doit être une adresse du client') end end def ca_remise_pre_order_ok if self.p_customer and self.p_customer_cat_id == 1 and self.wish_date and self.created_at and (self.wish_date >= (self.creation_date + 8.weeks)) true else false end end def ca_remise_qte_ok if self.p_customer and self.p_customer_cat_id == 1 true else false end end def ca_remise_enrobage_ok if !self.remise_ecole_ok and self.p_customer and self.p_customer.discount_enrobage true else false end end def ca_remise_ecole_ok if self.p_customer and self.p_customer_cat_id == 3 true else false end end def ca_discount_comptant if self.p_customer self.p_customer.discount_comptant end end after_save do self.price_lines.each do |pl| pl.save end end def ca_weight_tot r = 0.0 self.price_lines.each do |pl| r+= pl.weight_tot end return r end def ca_accounting_zone_id self.price_lineable.accounting_zone_id if self.price_lineable end def ca_accounting_zone_name self.price_lineable.accounting_zone_name if self.price_lineable end def ca_tot_lines_ht r = 0.0 self.price_lines.each do |pl| r+= pl.tot_amount_ht end return r end def ca_tot_lines_ht_for_discount r = 0.0 self.price_lines.each do |pl| r+= pl.tot_amount_ht if !pl.product_no_remise end return r end def ca_tot_lines_tva r = 0.0 self.price_lines.each do |pl| r+= pl.tot_amount_tva if pl end return r end def ca_tot_lines_tva_for_discount r = 0.0 self.price_lines.each do |pl| r+= pl.tot_amount_tva if !pl.product_no_remise end return r end def ca_tot_lines_ttc r = 0.0 self.price_lines.each do |pl| r+= pl.tot_amount_ttc end return r end def default_tot_fdp_ht return 0.0 # Skip default FDP if self.p_customer_cat_id == 2 if (self.tot_lines_ht + self.tot_discount_ht) >= 290.0 0.0 elsif (self.tot_lines_ht + self.tot_discount_ht) >= 150.0 15.0 else 30.0 end else if ca_weight_tot >= 45.0 0.0 elsif ca_weight_tot >= 30 25.0 else 40.0 end end end def ca_tot_fdp_ht if PriceLineBlock::PURCHASE_BLOCKS.include?(self.block_type) return 0.0 else if self.reliquat #false #self.price_lineable_type == "PriceDocument" and self.price_lineable and self.price_lineable.label == "Avoir" if self.reliquat_fdp_ht self.reliquat_fdp_ht else 0.0 end else if self.ct_tot_fdp_ht self.ct_tot_fdp_ht elsif self.bk_tot_fdp_ht self.bk_tot_fdp_ht else self.default_tot_fdp_ht end end end end def ca_fdp_tva_rate if self.accounting_zone_id == 1 20.0 else 0.0 end end def ca_tot_fdp_tva self.tot_fdp_ht * (self.fdp_tva_rate/100) end def ca_tot_fdp_ttc self.tot_fdp_ht + self.tot_fdp_tva end def ca_tot_discount_ht if self.ct_tot_discount_percent (self.ct_tot_discount_percent / -100.0) * self.ca_tot_lines_ht_for_discount else 0.0 end end def ca_tot_discount_tva if self.ct_tot_discount_percent (self.ct_tot_discount_percent / -100.0) * self.ca_tot_lines_tva_for_discount else 0.0 end end def ca_tot_discount_ttc self.tot_discount_ht + self.tot_discount_tva end def ca_tot_amount_af_discount_ht self.tot_lines_ht + self.tot_discount_ht + self.tot_fdp_ht end def ca_tot_amount_af_discount_tva self.tot_lines_tva + self.tot_discount_tva + self.tot_fdp_tva end def ca_tot_amount_af_discount_ttc self.tot_lines_ttc + self.tot_discount_ttc + self.tot_fdp_ttc end def ca_gen_discount_percent 0.0 end def ca_tot_gen_discount_ht 0.0 end def ca_tot_gen_discount_tva 0.0 end def ca_tot_gen_discount_ttc 0.0 end def ca_tot_amount_ht if self.ct_tot_amount_ht return self.ct_tot_amount_ht else self.tot_amount_af_discount_ht + self.tot_gen_discount_ht end end def ca_tot_amount_tva self.tot_amount_af_discount_tva + self.tot_gen_discount_tva end def ca_tot_amount_ttc self.tot_amount_af_discount_ttc + self.tot_gen_discount_ttc end def ca_payment_comptant if self.ct_payment_comptant self.ct_payment_comptant else if self.p_customer self.p_customer.comptant else nil end end end def ca_payment_delais if self.ct_payment_delais self.ct_payment_delais else if self.p_customer self.p_customer.payment_delais else nil end end end def ca_payment_month_end if self.ct_payment_month_end self.ct_payment_month_end else if self.p_customer self.p_customer.payment_fin_de_mois else nil end end end def ca_payment_end_at if self.price_lineable_type == "PriceDocument" and self.price_lineable start_date = self.price_lineable.date elsif self.price_lineable_type == "PCustomerSheet" and self.price_lineable start_date = self.price_lineable.price_line_block.creation_date end if start_date if self.payment_delais.to_i == 0 start_date else start_date + self.payment_delais.to_i.days end end end def ca_acompte if self.p_customer self.p_customer.acompte else nil end end def ca_acompte_percent if self.p_customer self.p_customer.acompte_percent else nil end end def ca_payment_days end def ca_nbr_ship if self.ct_nbr_ship self.ct_nbr_ship else nil end end def ca_creation_date if self.ct_creation_date self.ct_creation_date elsif self.price_lineable_type == "PriceDocument" and self.price_lineable and self.price_lineable.ref_element and self.price_lineable.ref_element.price_line_block self.price_lineable.ref_element.price_line_block.creation_date elsif self.id self.created_at else Date.today end end def ca_p_customer_cat_id if self.p_customer self.p_customer.p_customer_cat_id else nil end end def cumul_discount_ht r = 0.0 self.price_lines.each do |pl| r += pl.ca_tot_discount_ht end return r end AVOIR_TO_RESET = %w(weight_tot tot_lines_ht tot_lines_tva tot_lines_ttc tot_fdp_ht tot_fdp_tva tot_fdp_ttc tot_discount_ht tot_discount_tva tot_discount_ttc tot_amount_af_discount_ht tot_amount_af_discount_tva tot_amount_af_discount_ttc gen_discount_percent tot_gen_discount_ht tot_gen_discount_tva tot_gen_discount_ttc tot_amount_ht tot_amount_tva tot_amount_ttc nbr_ship) end