class Sale < CommLogistics::Base::Model::SuperiorStock
  include Comm::Module::Model::Logging
  include CommLogistics::Modules::MngAccounting
  include CommLogistics::Modules::MngProductsPrice
  include Comm::Module::NamedScope::TargetDate
  include CommLogistics::Modules::NamedScope::State
  include CommLogistics::Modules::NamedScope::TransType
  include CommLogistics::Modules::SetParams
  include CommLogistics::Modules::DateUtil

  has_many :sale_details, :dependent => :destroy
  has_many :child_details,
           :class_name => 'SaleDetail',
           :dependent => :destroy,
           :order => 'seq_number'
  belongs_to :customer
  belongs_to :area
  belongs_to :warehouse

  #Module::SetParamsをオーバーライド
  #売上データ作成(自動フロー用)
  def set_table_params(parent_ar, params)
    table_params = super(parent_ar, params)
    customer_ar = Customer.find(parent_ar.charge_customer_id.to_i)
    #締め日関係のデータを補完
    table_params[:sale_sheet_type_code] = customer_ar.sale_sheet_type_code
    table_params[:cutoff_type_code] = customer_ar.cutoff_type_code
    table_params[:cutoff_day_code] = customer_ar.cutoff_day_code
    table_params[:form_cutoff_date] = get_target_cutoff(table_params[:cutoff_type_code], table_params[:cutoff_day_code], Date.parse(table_params[:target_date])).to_s
    return table_params
  end
  
  def set_table_params_for_total_quantity(parent_ar,table_params)
    #売上以外は、基本、非在庫品はいらない。つまり、total_quantity=stock_total_quantityとなる。
    #売上だけ、total_quantityとstock_total_quantityが異なるので、オーバーライド
    #なにもしない。もうset_table_paramsで設定されているから。
  end
  
  def set_detail_params(parent_ar, table_params, params)
    parent_details = AcceptOrderDetail.find(:all, :conditions=>"accept_order_id=#{parent_ar.id}", :select=>@@common_detail_columns + ', IFNULL(price,0.0) AS price')
    if @table_rel['from_shipping_flag_code']==MCODE_FLAG_ON #売上 or サンプル
      #priceテーブルを作る
      accept_order_prices, non_stock_values = get_accept_order_price_table(parent_details)
      #出荷明細をとってくる
      shipping_details = get_shipping_rels(parent_ar,@@common_detail_columns)
      #priceテーブルから消し込んでいく
      return set_sale_details_from_shipping_details(shipping_details, accept_order_prices) + non_stock_values
    else
      return parent_details.only_hashfy
    end
  end
  #出荷明細から売上明細を作成するメソッド
  def set_sale_details_from_shipping_details(details, accept_order_prices)
    values = Array.new
    for j in 0..(details.length-1) do
      ##出荷明細データを1行づつとってくる。
      detail = details[j];
      ##売上だったら単価でも分割。まず出荷明細1行の数量をとる。
      shipping_quantity = detail['quantity'];
      ##出荷明細1行の数量を消化するまでループ
      while shipping_quantity > 0 do
        ##価格の内訳を取ってきて、出荷明細の1行の数量を消化して行く。
        prices = accept_order_prices[detail['product_id']];
        if prices.blank? 
          #あり得ないパターン
          return false
        end
        for k in 0..(prices[:array].length-1) do
          price_key = prices[:array][k]
          ##blankだったら、その価格のものは消化しきっている。
          if prices[:hash][price_key].blank?
            next
          end
          ##出荷明細1行の数量 <= 価格一つ分の数量  だったら出荷明細1行の数量は全て消化しきる。価格一つ分の数量は残る。
          if shipping_quantity <= prices[:hash][price_key] 
            tmp_rec = create_sale_detail(detail, shipping_quantity, price_key)
            prices[:hash][price_key] -= shipping_quantity;
            shipping_quantity=0;
          else
            ##出荷明細1行の数量 > 価格一つ分の数量  だったら出荷明細1行の数量は消化しきれない。価格一つ分の数量はすべて消化。
            tmp_rec = create_sale_detail(detail, prices[:hash][price_key], price_key)
            shipping_quantity -= prices[:hash][price_key];
            prices[:hash][price_key] = 0;
          end
          values.push(tmp_rec);
          ##価格キーに対する数量が0になったら、その価格キーを削除する。
          if prices[:hash][price_key] <= 0 
            prices[:hash].delete(price_key);
          end
          ##出荷数が0になったら、価格ハッシュのforを抜ける。
          if shipping_quantity <= 0 
            break
          end
        end
        ##価格ハッシュが空になったら出荷数量を消化することができないため、whileを抜ける。
        if prices[:hash].blank?
          break
        end
      end
    end
    return values
  end
  #出荷一行から売上一行を作るメソッド
  def create_sale_detail(detail, quantity, price)
      tmp_rec = detail.only_hashfy
      tmp_rec['quantity']=quantity
      tmp_rec['price']=price
      return tmp_rec
  end
  #受注の明細から、製品と製品の価格ごとに一覧表を作成する
  def get_accept_order_price_table(parent_details)
    accept_order_prices = Hash.new
    non_stock_values = Array.new
    for i in 0..(parent_details.length-1) do
      d_record = parent_details[i]
      if !accept_order_prices[d_record['product_id']]
        accept_order_prices[d_record['product_id']] = {:hash=>Hash.new, :array=>Array.new}
      end
      ##製品ごと、製品単価ごとに数量を記録する。
      tmp_price = d_record.price
      ##arrayを登録 (ヒットした順番を保証するため)
      if accept_order_prices[d_record['product_id']][:hash][tmp_price].blank?
        accept_order_prices[d_record['product_id']][:array].push(tmp_price);
      end
      accept_order_prices[d_record['product_id']][:hash][tmp_price] = (accept_order_prices[d_record['product_id']][:hash][tmp_price] || 0) + d_record['quantity']
      if d_record['stock_flag_code'].to_i == MCODE_STOCK_FLAG_OFF
        non_stock_values.push(d_record.only_hashfy);
      end
    end
    return accept_order_prices, non_stock_values
  end
  
protected
  def create_exec_do(main, details)
    if own_warehouse?(main)
      raise LOEMJ0001 + LOEMD0001
    end
    #check_params(main)
    # create sale
    self.attributes = main
    unless save
      raise EMJ0001 + EMD0001
    end
    #detailsはstock_infoより先に更新が必要
    update_details(self.id, details)
    update_stock_info(main, details)
    update_account_info(main, details)
    true
  end
  
  def destroy_exec_do
    details = child_details.ext_hashfy
    update_stock_info(self, details, false)
    update_account_info(self, details, false)
    true
  end
  
  def update_stock_info(main, details, is_create=true)
    if status_complete?(main)
      type = get_value_by_name(main, 'sale_type_code')
      trigger = get_value_by_name(main, 'sale_trigger_code')
      #if type != MCODE_SALE_TYPE_ADJUST && own_supplier?(main)
      if type != MCODE_SALE_TYPE_ADJUST && trigger != MCODE_SALE_TRIGGER_THROUGH
        # 価格調整 以外
        s_details = get_details_params_for_stock(main, details)
        calc_type = is_create ? CALC_SUB : CALC_ADD
        
        update_stock(main, s_details, calc_type)
        update_products_price(main, details, calc_type, false)
      end
    end
  end
  
  def update_account_info(main, details, is_create=true)
    if status_complete?(main)
      # 売掛、得意先元帳処理
      calc_type = is_create ? CALC_ADD : CALC_SUB
      update_accounting(calc_type, details)
    end
  end
  
#  def check_params(main)
#    type = get_value_by_name(main, 'sale_type_code')
#    #在庫調整以外で自社在庫でなければエラー
#    if type != MCODE_SALE_TYPE_ADJUST && !own_supplier?(main)
#      raise UserOperationError, LOEMJ0009+LOEMD0008
#    end
#  end
  
  # 経理関連処理
  def init_accounting(details)
    @acnt_target_id       = self.charge_customer_id
    @acnt_target_group_id = self.charge_customer_group_id
    @acnt_type            = MCODE_DEAL_TYPE_DEAL
    @acnt_timing          = self.charge_timing_code
    @acnt_target_class    = Customer
    @target_key = 'customer'
    
    @rp_cls        = Receivable
    @rp_detail_cls = ReceivableDetail
    
    @ledger_cls        = CustomerLedger
    @ledger_detail_cls = CustomerLedgerDetail
    @ledger_details = get_price_params_deal(details)
  end
  
  # 価格調整?
  def is_adjust?
    if self.sale_type_code == MCODE_SALE_TYPE_ADJUST
      return true
    else
      return false
    end
  end
  
  # 締め日チェック
  def cutoff_date_checks(main, details)
    super(main, details)
    #得意先締め日のチェック
    #gid = get_value_by_name(main, 'charge_customer_group_id')
    #cutoff_date_check(main, details, CustomerGroup.find(gid).cutoff_date_code)
    target_cutoff = get_value_by_name(main, 'form_cutoff_date')
    cutoff_date_check(main, details, nil, target_cutoff.to_date) unless target_cutoff.blank? #都度払いはチェックしない
  end
  
  #売上では、下記メソッドをmng_stockからオーバーライド。非在庫管理製品があった場合はskipする処理を加える
  def get_details_params_for_stock(main, details, overwrite_params={})
    results = []
    update_params = {}
    extract_lacking_params(update_params, main)
    logger.debug(["MNG_STOCK : get_details_params_for_stock",
                  "extract_params : #{update_params.inspect}",
                  "details : #{details.inspect}"].join("\n\t"))
    update_params.update(overwrite_params)
    details.each do |params|
      #非在庫管理品だったら在庫処理をスキップする
      unless params['stock_flag_code'] == MCODE_STOCK_FLAG_ON
        next
      end
      new_params = Marshal.load(Marshal.dump(params))
      new_params.update(update_params)
      results.push(new_params)
    end
    logger.debug(["get_details_params_for_stock",
                  "results : #{results.inspect}"].join("\n\t"))
    results
  end
end
