class Purchase < CommLogistics::Base::Model::SuperiorStock
  include Comm::Module::Model::Logging
  include CommLogistics::Modules::MngAccounting
  include CommLogistics::Modules::MngProductsPrice
  include CommLogistics::Modules::SetParams
  include CommLogistics::Modules::DateUtil
  include CommLogistics::Modules::FindPrice
  
  has_many :child_details,
           :class_name => 'PurchaseDetail',
           :dependent => :destroy,
           :order => 'seq_number'
  has_many :sales
  
  #Module::SetParamsをオーバーライド
  #仕入れデータ作成(自動フロー用)
  def set_table_params(parent_ar, params)
    table_params = super(parent_ar, params)
    sar = Supplier.find(parent_ar.supplier_id.to_i)
    table_params[:payto_supplier_id]=sar.payto_supplier_id
    supplier_ar = Supplier.find(sar.payto_supplier_id.to_i)
#    #金額を設定するために必要なパラメータ類
    table_params[:currency_type_code] = supplier_ar.currency_type_code
    table_params[:duty_type_code] = supplier_ar.duty_type_code
    table_params[:duty_rate] = supplier_ar.duty_rate
    table_params[:price_duty_type_code] = supplier_ar.price_duty_type_code
    table_params[:duty_calc_type_code] = supplier_ar.duty_calc_type_code
    table_params[:frac_digit_code] = supplier_ar.frac_digit_code
    table_params[:fraction_method_code] = supplier_ar.fraction_method_code
    table_params[:unit_price_fraction_method_code] = supplier_ar.unit_price_fraction_method_code
    table_params[:price_fraction_method_code] = supplier_ar.price_fraction_method_code
#    #締め日関係のデータを補完
    table_params[:cutoff_type_code] = supplier_ar.cutoff_type_code
    table_params[:cutoff_day_code] = supplier_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_detail_params(parent_ar, table_params, params)
    select = @@common_detail_columns + ", #{MCODE_STATUS2_COMP} AS inspect_status_code, quantity AS scheduled_quantity" + ', "" AS location_number'
    if @table_rel['from_shipping_flag_code']==MCODE_FLAG_ON #売上 or サンプル
      detail_params = get_shipping_rels(parent_ar,select).only_hashfy
    else
      detail_params = AcceptOrderDetail.find(:all, :conditions=>"accept_order_id=#{parent_ar.id} AND stock_flag_code=#{MCODE_STOCK_FLAG_ON}", :select=>select).only_hashfy
    end
    
    #find_priceで製品定価か仕入れ先別卸価格をサーチする。
    detail_params.each do |detail|
      detail['price'] = find_price(table_params, detail, InvoicePrice) || 0.0
    end
    #合計金額、消費税、赤黒区分上書き
    table_params[:total_duty] = CommLogistics::Tools::CalcDuty::calc_total_duty(detail_params, table_params)
    table_params[:total_price] = CommLogistics::Tools::CalcDuty::calc_total(detail_params, table_params)
    table_params[:sign_type_code] = (table_params[:total_price] >= 0) ? MCODE_SIGN_TYPE_BLACK : MCODE_SIGN_TYPE_RED
    return detail_params
  end
  
  def self.get_comp_total_quantity(id, par=nil)
    ar = find(id, :select=>"purchase_type_code, total_quantity, state_code")
    return ar && ar.state_code==MCODE_STATUS2_COMP ? ((ar.purchase_type_code==MCODE_PURCHASE_TYPE_EXTEND_UNLOAD || ar.purchase_type_code==MCODE_PURCHASE_TYPE_PROCESS_UNLOAD || ar.purchase_type_code==MCODE_PURCHASE_TYPE_DECOMPOSE_UNLOAD ? -1 : 1) * ar.total_quantity) : 0
  end
  
  #仕入処理は受注からと発注からの二通りあるため、場合分けする。order_idが無かった場合はsuperを見に行く
  def update_parent_rels(main)
    mid = main['manufacture_id'].to_i
    oid = main['order_id'].to_i
    if mid > 0
      rels = Manufacture.get_valid_rels(mid)
      rels_comp_total_quantity = 0
      rels.each do |rel|
        rels_comp_total_quantity = rels_comp_total_quantity + (eval("#{rel.table_name.classify}.get_comp_total_quantity(#{rel.record_id})"))
      end
      mar = Manufacture.find(mid)
      #そもそも発注処理で合計本数が0で登録された場合を考慮する。この場合記録するのは百分率
      rels_comp_total_quantity = rels.length==0 ? 0 : (rels_comp_total_quantity==0 ? 100 : 50) if mar.rels_total_quantity==0
      mar.update_attribute(:rels_comp_total_quantity,rels_comp_total_quantity)
    elsif oid > 0
      rels = Order.get_valid_rels(oid)
      rels_comp_total_quantity = 0
      #加工出庫と倉庫間移動出庫はマイナスの値となるが、発注はプラスなので、符号を反転させる必要が有る。
      rels.each do |rel|
        rels_comp_total_quantity = rels_comp_total_quantity + (eval("#{rel.table_name.classify}.get_comp_total_quantity(#{rel.record_id})"))
      end
      oar = Order.find(oid)
      #そもそも発注処理で合計本数が0で登録された場合を考慮する。この場合記録するのは百分率
      rels_comp_total_quantity = rels.length==0 ? 0 : (rels_comp_total_quantity==0 ? 100 : 50) if oar.rels_total_quantity==0
      oar.update_attribute(:rels_comp_total_quantity,rels_comp_total_quantity)
    else
      super
    end
  end
protected
  def create_exec_do(main, details)
#    if own_supplier?(main)
#      raise LOEMJ0010 + LOEMD0001
#    end
    # create purchase at first to get [id]
    self.attributes = main
    unless save
      raise EMJ0001 + EMD0001
    end
    update_details(self.id, details)
    update_account_info(main, details)
    
    update_stock_info(main, details)
    update_products_price_info(main, details)
    # arriving_idの更新があるかもなのでsave
    # loggingは不要なのでfalse指定で
    unless save(false)
      raise EMJ0001 + EMD0001
    end
    true
  end
  
  def destroy_exec_do
    details = child_details.ext_hashfy
    update_account_info(self, details, false)
    
    update_stock_info(self, details, false)
    update_products_price_info(self, details, false)
    true
  end
  
  def update_stock_info(main, details, is_create=true)
    type = get_value_by_name(main, 'purchase_type_code')
    trigger = get_value_by_name(main, 'purchase_trigger_code')
    
    if trigger == MCODE_PURCHASE_TRIGGER_ACCEPT_ORDER && type == MCODE_PURCHASE_TYPE_LENDING
      #貸出買取 => 自社倉庫の預り在庫を買い取りできるようにもしたtので、預り買取に名称変更
      # (-)out stock supplier = other
      s_details = get_details_params_for_stock(main, details)
      calc_type = is_create ? CALC_SUB : CALC_ADD
      if own_warehouse?(main)
        update_own_stock(main, s_details, calc_type)
      else
        update_stock(main, s_details, calc_type)
      end
      # (+)out stock supplier = own
      overwrite_params = {'supplier_id' => OWN_SUPPLIER_ID}
      s_details = get_details_params_for_stock(main, details, overwrite_params)
      calc_type = is_create ? CALC_ADD : CALC_SUB
      if own_warehouse?(main)
        update_own_stock(main, s_details, calc_type)
      else
        update_stock(main, s_details, calc_type)
      end
    elsif (trigger == MCODE_PURCHASE_TRIGGER_ACCEPT_ORDER && type == MCODE_PURCHASE_TYPE_NEW) ||
          (type == MCODE_PURCHASE_TYPE_STORING)
      #注文買取または入庫
      # (+)own stock
      if type == MCODE_PURCHASE_TYPE_NEW
        overwrite_params = {'supplier_id' => OWN_SUPPLIER_ID}
      else
        overwrite_params = {}
      end
      s_details = get_details_params_for_stock(main, details, overwrite_params)
      arriving = self.arriving_id ? Arriving.find(self.arriving_id) : Arriving.new
      
      #== @additionalのための処理
      begin
        if @additional and @additional["retry"]
          arriving.additional = @additional
        end
        if is_create
          arriving.create_arriving(main, details, s_details)
          self.arriving_id ||= arriving.id
        else
          arriving.delete_arriving(s_details)
        end
      rescue UserOperationError => e
        if arriving.additional
          @additional = arriving.additional
        end
        raise e
      end
      
    elsif type == MCODE_PURCHASE_TYPE_EXTEND_UNLOAD ||
          type == MCODE_PURCHASE_TYPE_EXTEND_LOAD ||
          type == MCODE_PURCHASE_TYPE_PROCESS_UNLOAD ||
          type == MCODE_PURCHASE_TYPE_PROCESS_LOAD ||
          type == MCODE_PURCHASE_TYPE_DECOMPOSE_UNLOAD ||
          type == MCODE_PURCHASE_TYPE_DECOMPOSE_LOAD
      #期限延長/加工
      # (+)own stock
      overwrite_params = {'supplier_id' => OWN_SUPPLIER_ID}
      s_details = get_details_params_for_stock(main, details, overwrite_params)
      calc_type = is_create ? CALC_ADD : CALC_SUB
      update_own_stock(main, s_details, calc_type)
    elsif type == MCODE_PURCHASE_TYPE_ADJUST || 
          trigger == MCODE_PURCHASE_TRIGGER_THROUGH || 
          trigger == MCODE_PURCHASE_TRIGGER_MANAGEMENT
      #ライブスルー・価格調整・自社管理在庫(売上で落とす) ー＞ 在庫処理なし
      logger.debug(["Good Through", main.inspect, details.inspect].join("\n\t"))
    else
      logger.error(["(error) unknown type or trigger", main.inspect, details.inspect].join("\n\t"))
      raise EMJ0001 + EMD0002
    end
  end
  
  #== 単価計算
  NOT_UPDATE_PRODUCTS_PRICE_TYPES=[MCODE_PURCHASE_TYPE_ADJUST,
                                   MCODE_PURCHASE_TYPE_STORING,
                                   MCODE_PURCHASE_TYPE_PROCESS_UNLOAD,
                                   MCODE_PURCHASE_TYPE_DECOMPOSE_UNLOAD,
                                   MCODE_PURCHASE_TYPE_DECOMPOSE_LOAD]
  NOT_UPDATE_PRODUCTS_PRICE_TYPES_IF_OWN_SUPPLIER=[MCODE_PURCHASE_TYPE_EXTEND_UNLOAD,
                                                   MCODE_PURCHASE_TYPE_EXTEND_LOAD]
  def update_products_price_info(main, details, is_create=true)
    Rails.logger.debug("■DEBUG: START PP")
    trigger = get_value_by_name(main, 'purchase_trigger_code')
    # 販売管理の場合は単価更新なし
    if trigger == MCODE_PURCHASE_TRIGGER_THROUGH
      return
    end
    type = get_value_by_name(main, 'purchase_type_code')
    supplier_id = get_value_by_name(main, 'supplier_id')
    calc_type = is_create ? CALC_ADD : CALC_SUB
    if type == MCODE_PURCHASE_TYPE_PROCESS_LOAD
      # 加工入庫
      Rails.logger.debug("■DEBUG:PP PROCESS_LOAD")
      update_products_price_process_load(main, details, calc_type)
    elsif !NOT_UPDATE_PRODUCTS_PRICE_TYPES.include?(type) && 
          !(NOT_UPDATE_PRODUCTS_PRICE_TYPES_IF_OWN_SUPPLIER.include?(type) && 
            supplier_id == OWN_SUPPLIER_ID)
      # 価格調整、入庫、加工出庫 以外
      # 倉庫間移動の仕入先が自社以外　の場合
      Rails.logger.debug("■DEBUG: PP")
      update_products_price(main, details, calc_type)
    end
  end
  
  #== 買掛計算
  NOT_UPDATE_ACCOUNT_TYPES=[MCODE_PURCHASE_TYPE_EXTEND_UNLOAD,
                            MCODE_PURCHASE_TYPE_EXTEND_LOAD,
                            MCODE_PURCHASE_TYPE_STORING,
                            MCODE_PURCHASE_TYPE_PROCESS_UNLOAD,
                            MCODE_PURCHASE_TYPE_DECOMPOSE_UNLOAD,
                            MCODE_PURCHASE_TYPE_DECOMPOSE_LOAD]
  def update_account_info(main, details, is_create=true)
    Rails.logger.debug("■DEBUG: START AI")
    type = get_value_by_name(main, 'purchase_type_code')
    supplier_id = get_value_by_name(main, 'supplier_id')
    # 状態完で、期限延長/入庫/加工出庫以外または加工仕入が自社の場合以外
    if status_complete?(main)
      if !NOT_UPDATE_ACCOUNT_TYPES.include?(type) && 
         !(type == MCODE_PURCHASE_TYPE_PROCESS_LOAD && supplier_id == OWN_SUPPLIER_ID)
        # 売掛、得意先元帳処理
        Rails.logger.debug("■DEBUG: AI")
        calc_type = is_create ? CALC_ADD : CALC_SUB
        update_accounting(calc_type, details)
      end
    end
  end
  
  def get_details_params_for_stock(main, details, overwrite_params={})
    results = []
    update_params = {}
    extract_lacking_params(update_params, main)
    update_params.update(overwrite_params)
    type = get_value_by_name(main, 'purchase_type_code')
    error_array=[]
    details.each do |params|
      #sales/purchase以外で非在庫管理製品があった場合はエラーを出力する
      unless params['stock_flag_code'] == MCODE_STOCK_FLAG_ON
        if NOT_UPDATE_ACCOUNT_TYPES.include?(type)
          error_array << Product.find(params['product_id']).disp_name
        end
        next
      end
      
      #検品=未完了ならquantityを0にしてstocksにレコードだけでも生成するため、nextでスキップするのはやめました。
#      unless params['inspect_status_code'] == MCODE_STATUS2_COMP
#        next
#      end
      new_params = Marshal.load(Marshal.dump(params))
      new_params.update(update_params)
      
      #検品=未完了ならquantityを0にしてstocksにレコードだけでも生成する。
      #在庫マスターに未検品を含めた実数量を表示できるようにこの仕様とした。
      unless params['inspect_status_code'] == MCODE_STATUS2_COMP
        new_params.update({'quantity' => 0})
      end
      
      results.push(new_params)
    end
    unless error_array.blank?
      #エラー
      msg = LOEMJ0002+LOEMD0020+error_array.join(', ')
      raise UserOperationError, msg
    end
    results
  end
  
  # 経理関連処理
  def init_accounting(details)
    @acnt_target_id       = self.payto_supplier_id
    @acnt_target_group_id = self.payto_supplier_group_id
    @acnt_type            = MCODE_DEAL_TYPE_DEAL
    @acnt_timing          = self.charge_timing_code
    @acnt_target_class    = Supplier
    @target_key = 'supplier'
    
    @rp_cls        = Payable
    @rp_detail_cls = PayableDetail
    
    @ledger_cls        = SupplierLedger
    @ledger_detail_cls = SupplierLedgerDetail
    @ledger_details = get_price_params_deal(details)
  end
  
  # 価格調整?
  def is_adjust?
    if self.purchase_type_code == MCODE_PURCHASE_TYPE_ADJUST
      return true
    else
      return false
    end
  end
  
  #== 締め日チェック
  def cutoff_date_checks(main, details)
    unless details
      details = self.child_details
    end
    @completed_detail_exists = false
    details.each do |detail|
      get_value_by_name(detail, 'inspect_status_code')
      if get_value_by_name(detail, 'inspect_status_code') == MCODE_STATUS2_COMP
        @completed_detail_exists = true
      end
    end
    
    super(main, details)
    #仕入先締め日のチェック
    #gid = get_value_by_name(main, 'payto_supplier_group_id')
    #cutoff_date_check(main, details, SupplierGroup.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
  def cutoff_check_target?(main)
    #仕入は検品完で在庫が動くので。
    return true
  end
  def cutoff_date_error(main)
    if User.root_user?(@session[:user_id].to_i)
      Rails.logger.info("[INFO]cutoff error canceled by root user.<br>"+main.inspect)
      return
    end
    #完のときと検品完があるとき
    if status_complete?(main) || @completed_detail_exists
      raise UserOperationError, LOEMJ0014+LOEMD0016+LOEMD0017
    end
  end
  #== 締めテーブルチェック
  #仕入だけはpayablesをみる
  def get_close_table_key
    return 'payables'
  end
end