module CommLogistics::Modules::Print
  #= コントローラ用 PDF出力
  module Controller
    #== 帳票出力メイン
    #コントローラインタフェース
    def print_sheet(pdf_cls=@pdf_cls)
      resp={}
      ActiveRecord::Base.transaction do
        pdfout = pdf_cls.new(params, @mcls)
        pdfout.make_sheet
        pdfout.page_to_pdf
        
        resp[:success] = true
        resp[:file_name] = pdfout.get_file_name
        resp[:tmp_file_name] = pdfout.get_tmp_path
        resp[:file_type] = pdfout.get_file_type
        
        pdfout.post_update if pdfout.respond_to?(:post_update)
      end
      respond_to do |format|
        format.json { render :json => resp.to_json }
      end
    rescue Exception => e
      Rails.logger.error(e.message + "\n" + e.backtrace.join("\n"))
      resp.update({:success=>false, :message=>e.message})
      respond_to do |format|
        format.json { render :json => resp.to_json }
      end
    end
    #== print_sheet呼び出し前に実装クラスで処理をしたい場合のためのalias
    alias :print_sheet_base :print_sheet
    
    #== PDF出力基底クラス
    class PdfOut
      include CommLogistics::Const::Code
      include CommLogistics::Const::Error
      include Comm::Const::Print
      include IText
      
      # ubd 関連定数
      STR_UBD_LABEL='ubd'
      STR_NON_ARG_UBD_DB='9999-00-00'
      STR_NON_ARG_UBD_DISP='N/A'
      
      def initialize(params, mcls=nil)
        @params = params
        @mcls = mcls if mcls
        @regex_3digit_comma = Regexp.new('(\d)(?=(?:\d\d\d)+(?!\d))')
        @regex_date_str = Regexp.new('^\d{4}-\d{2}-\d{2}$')
        @regex_number_key_str = Regexp.new('(price|duty|quantity)$')
        @regex_price_key_str = Regexp.new('(price|duty)$')
        #時間記録関連
        @update_time_column_symbol ||= :print_sheet_date
        @cancel_auto_update_time ||= false
        @record_update_time ||= DateTime.now.to_s
      end
      
      def make_sheet
        @pdata =  make_sheet_sequential
      rescue Exception => e
        Rails.logger.error(error_out(e))
        raise e
      end
      
      def page_to_pdf(result_paging=@pdata)
        options = {}
        options[:searchdir] = "templates"
        if defined?(@pdf_basename) && @pdf_basename
          options[:template_naming_base] = @pdf_basename
        else
          options[:template_naming_base] = "#{@mcls.name.underscore}"
        end
        to_pdf(result_paging, options)
      end
      
      def get_file_name(opts={})
        opts[:basename] ||= @pdf_basename ? @pdf_basename : @mcls.name.underscore
        opts[:uniqstr] ||= Time.now.strftime("%y%m%d-%H%M")
        opts[:extension] ||= '.pdf'
        return opts[:basename]+'-'+opts[:uniqstr]+opts[:extension]
      end
      
      def get_tmp_path
        return @tmp_path
      end
      
      def get_file_type
        return FILE_TYPE_INFO[1][:ctype]
      end
      
      def make_filename
        return @mcls.name.underscore
      end
      
      #=== PDF出力
      # * iTextのオーバーライド
      # * テンプレートがページ情報のhashで（ページごとに）指定できます
      #  * pdfの基本名（@mcls.name.underscoreがデフォルト、@pdf_basenameで明示指定）がhogeの場合、
      #   * 無指定...hoge.pdf
      #   * :page_info => {:head_page => true}...hoge_header.pdf
      #   * :page_info => {:sheet_type => 1}...hoge_1.pdf
      #
      def to_pdf(data, options={})
        options[:searchdir] ||= params[:controller]
        options[:searchpath] = File.expand_path(File.join(RAILS_ROOT, 'app','views', options[:searchdir]))
        pdf = ITextPrinter.new(options)
        @tmp_path = Dir.tmpdir+'/'+rand(100000).to_s+Time.now.to_i.to_s
        pdf.outfile = @tmp_path
        pdf.document_start
        insert_pdf_page_with_template(pdf, data, options)
        pdf.document_end
        #pdf.display
      end
      
      def insert_pdf_page_with_template(pdf, data, options)
        basename = options[:template_naming_base]
        last_template = ''
        tmp = []
        data_ary = []
        data.each do |dt|
          #使用するテンプレート名生成
          tmp << basename
          template_name = make_template_name(dt, tmp)
          tmp.clear
          #Rails.logger.debug('to_pdf:'+options[:template]+"\n"+dt.inspect)
          if !last_template.blank? && (last_template != template_name)
            options[:template] = last_template
            begin
              pdf.insert_page(data_ary, options)
            rescue => e
              #テンプレートがないかもしれない場合のエラー
              Rails.logger.error(error_out(e))
              raise LOEMJ0012+LOEMD0014
            end
            data_ary.clear
          end
          data_ary << dt
          last_template = template_name
        end
        unless data_ary.length == 0
          options[:template] = last_template
          pdf.insert_page(data_ary, options)
        end
      end
      
      def make_template_name(dt, tmp=[])
        if dt.include?(:page_info)
          if dt[:page_info].include?(:basename)
            tmp[0] = dt[:page_info][:basename]
          end
          if dt[:page_info].include?(:head_page)
            tmp << "_header"
          end
          if dt[:page_info].include?(:sheet_type)
            tmp << "_#{dt[:page_info][:sheet_type]}"
            if dt[:page_info].include?(:next_page) && dt['page_num'] > 1
              tmp << "_next"
            end
          end
        end
        tmp << ".pdf"
        return tmp.join
      end
      
      def get_hash_with_disp_name(ar)
        arh = [ar.only_hashfy]
        arh.add_disp_names()
        return arh[0]
      end
      
      def add_disp_name_to_hash(hash)
        arh = [hash]
        ##hogehoge_dnをdisp_nameから任意のカラムから取ってくることもできる config.jsで設定する。
        opt = @params[:print_disp_keys].blank? ? {} : {:disp_keys => @params[:print_disp_keys]}
        arh.add_disp_names(opt)
        return format_value(format_price(arh[0], ['price']))
      end
      
      def get_output_date
        @formatted_current_time ||= Time.now.strftime("%Y/%m/%d %H:%M:%S")
        return @formatted_current_time
      end
      
      def error_out(e)
        errors = ["\n>>>>> ERROR INFO"]
        errors << Time.now.to_s
        errors << e.message
        errors << @session.inspect if @session
        errors << e.backtrace
        return errors.join("\n")
      end
      
      #== 顧客(など)情報をHashに取得
      #[ar]Customer/SupplierモデルのActiveRecordオブジェクト
      #[opts]オプション
      def get_address_info(ar, opts={})
        opts = {:title=>true, :avoid_delivery_info=>false}.merge(opts)
        title = opts[:title] ? Comm::Tool::DispName.get_code_disp_names('title_code', [], MFIND_V, [ar.title_code]).first['disp_name'] : ''
        if opts[:avoid_delivery_info] || !ar.respond_to?("delivery_zip") || (ar.delivery_zip.blank? && ar.delivery_address_1.blank?)
          zip = (ar.zip || "").strip
          tel = (ar.tel || "").strip
          fax = (ar.fax || "").strip
          address_1 = (ar.address_1 || "").strip
          address_2 = (ar.address_2 || "").strip
          address_3 = (ar.address_3 || "").strip
        else
          zip = (ar.delivery_zip || "").strip
          tel = (ar.delivery_tel || "").strip
          fax = (ar.delivery_fax || "").strip
          address_1 = (ar.delivery_address_1 || "").strip
          address_2 = (ar.delivery_address_2 || "").strip
          address_3 = (ar.delivery_address_3 || "").strip
        end
        if ar.respond_to?('print_name_1')
          print_name_1 = (ar.print_name_1 || "").strip 
          print_name_2 = (ar.print_name_2 || "").strip
        else
          print_name_1 = (ar.j_name || "").strip 
          print_name_2 = ""
        end
        disp_name = (ar.disp_name || "").strip
        if ar.respond_to?("contact_name")
          contact_name = ar.contact_name
        else
          contact_name = ""
        end
        info = {'zip' => '〒'+zip,
                'address_1' => address_1,
                'address_2' => address_2,
                'address_3' => address_3,
                'address_1_2' => address_1 + ' ' + address_2,
                'address_2_3' => address_2 + ' ' + address_3,
                'address' => ['〒'+zip,
                              address_1, 
                              address_2, 
                              address_3].join(' '),
                'name' => [print_name_1,
                           print_name_2,
                           '  ',
                           title].join(' '),
                'name_1' => print_name_1 + (print_name_2.blank? ? ('  ' + title) : ''),
                'name_2' => print_name_2 + (print_name_2.blank? ? '' : ('  ' + title)),
                'disp_name' => disp_name + '  ' + title,
                'tel' => 'TEL '+tel,
                'fax' => 'FAX '+fax,
                'telfax' => ['TEL '+tel, 'FAX '+fax].join('  '),
                'contact_name' => contact_name}
                
        return info
      end
      
      def set_value_to_pdata(pdata, page, cnt, key, val, rec={})
        pdata[page] ||= {}
        pdata[page][@pdf_detail_name] ||={}
        pdata[page][@pdf_detail_name][cnt] ||= {}
        pdata[page][@pdf_detail_name][cnt][key] = format_value(key, val, rec)
      end
      
      def format_value(key, val, rec={})
        @format_columns ||= @total_columns
        if @format_columns.include?(key)
          str_val = val.to_i.to_s
          #return str_val.reverse.gsub(/(\d{3})(?=\d)/, '\1,').reverse
          return str_val.gsub(@regex_3digit_comma, '\1,')
        end
        if key == STR_UBD_LABEL && val == STR_NON_ARG_UBD_DB
          return STR_NON_ARG_UBD_DISP
        end
        return val
      end
    end
    
    #== 親ID配列を回して情報を順次そろえていくようなPDF出力
    # * 取引系のコントローラでID指定を受けて使用されることを想定しています
    # * 売上伝票,指示書とか..
    class PdfBase < PdfOut
      def initialize(params, mcls=nil)
        @ids = params[:id_list].split(",")
        super
      end
      
      #=== PDF作成
      #[id_list] 作成対象ID配列
      def make_sheet_sequential
        id_list = @ids
        #１ページあたりの明細レコード数(のデフォルト)
        @print_rec_num_per_sheet ||= 5
        detail_name = @pdf_detail_name ? @pdf_detail_name : @mcls.name.underscore+'_detail'
        
        pdata = {}
        page = 0
        id_list.each do |id|
          ar = @mcls.find(id)
          #伝票種類毎に１ページあたりの明細レコード数の変更する
          set_print_rec_num_per_sheet(ar)
          #明細の順番を変えたい場合は下記
          if respond_to?(:get_arranged_child_details)
            details = get_arranged_child_details(ar)
          else
            details = ar.child_details
          end
          #1親レコードが作る全ページ(に相当するhash)をpdataに生成して共通要素(親情報)を設定
          make_all_pages_by_parent(ar, pdata, details, page)
          #各ページの要素を設定
          page = set_details_to_pages(ar, pdata, details, detail_name, page)
          make_sheet_on_change_parent(ar, pdata, page)
          #時間記録
          unless @cancel_auto_update_time
            unless ar.update_attributes(@update_time_column_symbol => @record_update_time)
              raise 'Time logging failed'
            end
          end
        end
        return arrange_to_array(pdata)
      end
      
      #ページ数を定義するためにオーバーライド要
      def set_print_rec_num_per_sheet(ar)
        #下記３データの定義必須
        #@print_rec_num_per_sheet = N
        #@print_rec_num_per_sheet_second = N (２枚目があるときのみ)
        @print_rec_other_info_num = 0
      end
      
      def make_all_pages_by_parent(ar, pdata, details, page)
        end_page = get_end_page_number(ar, details, page)
        org_page_num = page - 1
        for i in page..end_page
          main_hash = ar.only_hashfy
          main_hash.update(make_sheet_add_for_main(ar))
          main_hash.update({'page_total' => end_page - org_page_num, 'page_num' => i - org_page_num})
          pdata[i] =  add_disp_name_to_hash(main_hash)
        end
      end
      
      def arrange_to_array(pdata)
        data_array = []
        pdata.each{|key, val| data_array[key]=val}
        return data_array
      end
      
      def set_details_to_pages(ar, pdata, details, detail_name, page)
        seq = 0
        print_rec_num_index =  @print_rec_num_per_sheet - 1
        details.each do |d|
          pdata[page][detail_name] ||= {}
          details_hash = d.only_hashfy
          details_hash.update(make_sheet_add_for_detail(ar, d))
          pdata[page][detail_name][seq] = add_disp_name_to_hash(details_hash)
          
          if seq == print_rec_num_index
            #子レコード数で改ページ
            pdata[page].update(make_sheet_add_on_page_end(ar, pdata, page))
            seq = 0
            page += 1
            if check_two_sheet(ar) && @print_rec_num_per_sheet_second
              print_rec_num_index = @print_rec_num_per_sheet_second - 1
            end
          else
            seq += 1
          end
        end
        
        #子レコード数が半端な場合、改ページの処理
        unless seq == 0
          pdata[page].update(make_sheet_add_on_page_end(ar, pdata, page))
          page += 1
        end
        return page
      end
      
      def get_end_page_number(ar, details, page)
        unless @print_rec_other_info_num
          @print_rec_other_info_num = 0 #念のため
        end
        #1ページあたりの明細レコード数を考慮したページ数
        #現ページ数に商を足して、終了ページを求める
        #割りきれた場合は1多くなっているハズなので減算
        if details.length > 0
          #２枚目があるとき
          if check_two_sheet(ar) && (details.length > (@print_rec_num_per_sheet - @print_rec_other_info_num))
            val = (details.length + @print_rec_other_info_num - @print_rec_num_per_sheet).divmod(@print_rec_num_per_sheet_second)
            val[0] += 1
          else
            val = (details.length + @print_rec_other_info_num).divmod(@print_rec_num_per_sheet)
          end
          add = val[0]
          if val[1] == 0 && !defined?(@exists_extra_column)
            add = add - 1
          end
          end_page = page + add
        else
          end_page = page
        end
        return end_page
      end
      
      #２枚目のフォーマットがある際には各伝票出力コントローラーでオーバーライドしてtrueを返すよう実装
      def check_two_sheet(ar)
        return false
      end
      
      def supplement_buyer_and_taker(ar,add)
        add['buyer_zip'] = '〒'+ ar['buyer_zip']
        add['taker_zip'] = '〒'+ ar['taker_zip']
        title = Comm::Tool::DispName.get_code_disp_names('title_code', [], MFIND_V, [Customer.find(ar.customer_id).title_code]).first['disp_name']
        add['buyer_disp_name'] = [ar['buyer_last_name'],ar['buyer_first_name'],title].join('  ')
        add['taker_disp_name'] = [ar['taker_last_name'],ar['taker_first_name'],title].join('  ')
        #届け先情報がない場合には購入者情報を設定する
        if ar['taker_flag'] == MCODE_STOCK_FLAG_OFF
          add['taker_first_name'] = ar['buyer_first_name']
          add['taker_last_name'] = ar['buyer_last_name']
          add['taker_first_kana_name'] = ar['buyer_first_kana_name']
          add['taker_last_kana_name'] = ar['buyer_last_kana_name']
          add['taker_disp_name'] = add['buyer_disp_name']
          add['taker_tel'] = ar['buyer_tel']
          add['taker_fax'] = ar['buyer_fax']
          add['taker_zip'] = add['buyer_zip']
          add['taker_address_1'] = ar['buyer_address_1']
          add['taker_address_2'] = ar['buyer_address_2']
        end
        return add
      end
      
      #下位クラスの伝票宛名情報を購入者・届け先情報で上書きする
      def overtype_send_info(ar,send_info,str,tar)
        title = Comm::Tool::DispName.get_code_disp_names('title_code', [], MFIND_V, [tar.title_code]).first['disp_name']
        #マスター登録されていない顧客で、購入者と届け先が別の場合と届け先のみが設定されている場合
        if ar.taker_flag == MCODE_STOCK_FLAG_ON
          send_info[str]['zip'] = '〒'+ ar['taker_zip']
          send_info[str]['address_1'] = ar['taker_address_1']
          send_info[str]['address_2'] = ar['taker_address_2']
          send_info[str]['address'] = ['〒'+ ar['taker_zip'],ar['taker_address_1'],ar['taker_address_2']].join(' ')
          send_info[str]['name_1'] = [ar['taker_last_name'],title].join('  ')
          send_info[str]['name_2'] = [ar['taker_first_name'],title].join('  ')
          send_info[str]['name'] = [ar['taker_last_name'],ar['taker_first_name'],title].join('  ')
          send_info[str]['disp_name'] = send_info[str]['name']
          send_info[str]['tel'] =  'TEL ' + ar['taker_tel']
          send_info[str]['fax'] = 'FAX '+ ar['taker_fax'],
          send_info[str]['telfax'] = ['TEL ' + ar['taker_tel'], 'FAX '+ ar['taker_fax']].join('  ')
        #マスター登録されていない顧客で、購入者のみが設定されている場合
        elsif ar.buyer_flag == MCODE_STOCK_FLAG_ON
          send_info[str]['zip'] = '〒'+ ar['buyer_zip']
          send_info[str]['address_1'] = ar['buyer_address_1']
          send_info[str]['address_2'] = ar['buyer_address_2']
          send_info[str]['address'] = ['〒'+ ar['buyer_zip'],ar['buyer_address_1'],ar['buyer_address_2']].join(' ')
          send_info[str]['name_1'] = [ar['buyer_last_name'],title].join('  ')
          send_info[str]['name_2'] = [ar['buyer_first_name'],title].join('  ')
          send_info[str]['name'] = [ar['buyer_last_name'],ar['buyer_first_name'],title].join('  ')
          send_info[str]['disp_name'] = send_info[str]['name']
          send_info[str]['tel'] =  'TEL ' + ar['buyer_tel']
          send_info[str]['fax'] = 'FAX '+ ar['buyer_fax'],
          send_info[str]['telfax'] = ['TEL ' + ar['buyer_tel'], 'FAX '+ ar['buyer_fax']].join('  ')
        end
        return send_info
      end
      #--
      #呼び出し元クラス、その**detailのカラム以外の項目を
      #いじるためのoverrideして実装するメソッド群
      #返り値Hashの場合はそのまま応答のhashにupdateされます
      #++
      #== 親レコード用
      def make_sheet_add_for_main(ar)
        {}
      end
      #== 子レコード用
      def make_sheet_add_for_detail(ar, detail_ar)
        {}
      end
      #== 1ページ単位の追加項目
      def make_sheet_add_on_page_end(ar, pdata, page)
        {}
      end
      #== 1親レコード終了時の処理
      def make_sheet_on_change_parent(ar, pdata, page)
        true
      end
      
      #== 金額フォーマッタ
      #hashの"*price"キーの値にカンマ入れたりなど
      #[hash] 検索対象のhash
      def format_price(hash, allow_flaction=[])
        keys = hash.keys.select{|val| val=~@regex_number_key_str}
        keys.each do |key|
          if hash[key]
            if allow_flaction.include?(key)
              val = hash[key].to_f % 1 > 0 ? sprintf("%.3f", hash[key]) : sprintf("%.0f",hash[key])
            else
              val = hash[key].to_i.to_s
            end
            #hash[key] = val.reverse.gsub(/(\d{3})(?=\d)/, '\1,').reverse
            hash[key] = val.gsub(@regex_3digit_comma, '\1,')
            if defined?(@currency_type_name) && key=~@regex_price_key_str
              hash[key] = @currency_type_name+hash[key]
            end
          end
        end
        return hash
      end
      
      def format_value(hash)
        hash.keys.each do |key|
          val = hash[key].to_s
          if key == STR_UBD_LABEL && val == STR_NON_ARG_UBD_DB
            hash[key] = STR_NON_ARG_UBD_DISP
          end
        end
        return hash
      end
    end
    
    #== 揃った情報をページングする（だけ）のPDF出力
    # * 各種リストコントローラからの（検索結果を利用した）使用を想定しています
    # * 受取手形一覧など
    class PdfList < PdfOut
      def initialize(params, mcls=nil)
        @pdf_detail_name = 'detail'
        @print_line_num_per_sheet ||= 36
        @index_per_sheet = @print_line_num_per_sheet - 1
        @total_columns ||= []
        super
      end
      
      def make_sheet_sequential
        #データhash、総ページ数を作成／取得
        pdata, page = get_data_hash
        #配列に直しつつ、共通データなどを設定(ページ数は0originなので出力データ化するときに+1です)
        data_array = []
        doc_info = get_document_wide_info(page + 1)
        pdata.each do |index, rec_hash|
          rec_hash.update(doc_info)
          rec_hash['page_num'] = index + 1
          data_array[index]=rec_hash
        end
        return data_array
      end
      
      def get_data_hash
        if defined?(@paging_column)
          return get_data_hash_with_column_paging
        else
          return get_data_hash_series
        end
      end
      
      #=== カラムページングなし
      def get_data_hash_series
        record_array = @params[:record_list].ext_hashfy(:disp=>{:multiple_id_cols => (@multiple_id_cols||{}), :disp_keys => @params[:print_disp_keys]||{} })
        pdata = {}
        page = 0
        cnt = 0
        record_array.each do |rec|
          page, cnt = record_array_to_pdata(pdata, rec, page, cnt)
        end
        
        if defined?(@total_columns)
          #総計の設定
          @sub_totals.each do |k, v|
            #Rails.logger.debug('total at NOT paging - key:'+k.to_s+' val:'+v.to_s)
            set_value_to_pdata(pdata, page, cnt, k, v)
          end
          set_value_to_pdata(pdata, page, cnt, @total_title_column, '総計')
        else
          page -= 1 if cnt == 0
        end
        return pdata, page
      end
      
      #=== カラムによるページングあり
      # @paging_column, @total_columnsが設定されて
      # @paging_columnのカラムでソートされたデータの入力が前提です
      #
      def get_data_hash_with_column_paging
        record_array = @params[:record_list].ext_hashfy(:disp=>{:multiple_id_cols => (@multiple_id_cols||{}), :disp_keys => @params[:print_disp_keys]||{} })
        
        #ページングしたhashを作成
        pdata, page, cnt = {}, 0, 0
        record_array.each_with_index do |rec, ind|
          #マスタセット
          if ind==0 || (record_array[ind-1][@paging_column] != rec[@paging_column])
            set_master_wide_info(rec)
          end
          page, cnt = record_array_to_pdata(pdata, rec, page, cnt)
          next_ind = ind+1
          if (record_array[next_ind] && (rec[@paging_column] != record_array[next_ind][@paging_column])) ||
              !record_array[next_ind]
            #ページングカラムが変わることによる改ページ
            #小計の設定
            @sub_totals.each do |k, v|
              set_value_to_pdata(pdata, page, cnt, k, v)
              @grand_totals ||= {}
              accumulate_vals(@grand_totals, k, v)
            end
            set_value_to_pdata(pdata, page, cnt, @total_title_column, '小計')
            @sub_totals.clear
            #改ページ
            page, cnt = count_to_next_record_with_opts(pdata, page, cnt, false, true)
          end
        end
        
        #最後に常に1足されてしまうので、、
        page -= 1
        #総計の設定
        cnt = pdata[page][@pdf_detail_name].length - 1
        page, cnt = count_to_next_record_with_opts(pdata, page, cnt)
        @grand_totals.each do |k, v|
          set_value_to_pdata(pdata, page, cnt, k, v)
        end
        set_value_to_pdata(pdata, page, cnt, @total_title_column, '総計')
        return pdata, page
      end
      
      def set_master_wide_info(rec)
        Rails.logger.debug('rec:'+rec.inspect)
        if defined?(@paging_disp_column)
          paging_disp_colum = rec[@paging_disp_column]
        else
          paging_disp_colum = rec[@paging_column]
        end
        @master_wide_info = {'paging_disp' => paging_disp_colum}
      end
      
      def record_array_to_pdata(pdata, rec, page, cnt)
        rec.each do |k, v|
          set_value_to_pdata(pdata, page, cnt, k, v)
          if @total_columns.include?(k)
            @sub_totals ||= {}
            accumulate_vals(@sub_totals, k, v)
          end
        end
        return count_to_next_record_with_opts(pdata, page, cnt)
      end
      
      #
      # * change_masterは、マスター毎にトータルページ数を計算して設定します。
      #  * 1印刷丸ごとの通しページ数でOKな場合は、change_masterは使用せずに全ページ作成後にまとめて設定します。
      #  * その際の強制改ページはnext_pageを使用します。
      #
      def count_to_next_record_with_opts(pdata, page, cnt, change_master=false, next_page=false)
        @start_page_index = 0 if page == 0
        if cnt == @index_per_sheet || change_master || next_page
          pdata[page].update(@master_wide_info) if defined?(@master_wide_info)
          pdata[page].update({'page_num'=>(page - @start_page_index + 1)})
          page += 1
          cnt = 0
          if change_master
            page_total = page - @start_page_index
            for i in @start_page_index..(page)
              pdata[i].update({'page_total'=>page_total}) if pdata[i]
            end
            @start_page_index = page
          end
        else
          cnt += 1
        end
        return page, cnt
      end
      
      def accumulate_vals(hash, key, val)
        hash ||= {}
        if hash.include?(key)
          hash[key] += val.to_i
        else
          hash[key] = val.to_i
        end
      end
      
      def get_document_wide_info(total_page_num=nil)
        start_month = Date.parse(@params[:start_target_date]).strftime("%Y年 %m月")
        ret_hash = {'start_target_date' => @params[:start_target_date],
                    'start_target_month' => start_month,
                    'end_target_date' => @params[:end_target_date],
                    'output_date' => get_output_date}
        if total_page_num
          ret_hash['page_total'] = total_page_num
        end
        if defined?(@document_wide_additional_info)
          ret_hash.update(@document_wide_additional_info)
        end
        return ret_hash
      end
      
      def make_filename
        return @pdf_basename
      end
    end
    
    #== リストを利用しつつもカラムが歯抜けになったりする変則的なもの
    # * 得意先／仕入先元帳、請求書など
    class PdfLedger < PdfList
      def initialize(params, mcls=nil)
        super
      end
      
      # 継承先でone_masterを定義してべったりと実装してください
      def make_sheet_sequential
        req_hash = @params[:record_list]
        pdata, page, cnt = {}, 0, 0
        @params[:id_list].split(",").each do | id |
          rec_array = req_hash[id.to_i]
          if rec_array.length == 0
            next
          end
          page, cnt = one_master(pdata, page, cnt, rec_array)
          
          #時間記録
          unless @cancel_auto_update_time
            ar = @mcls.find(id)
            unless ar.update_attributes(@update_time_column_symbol => @record_update_time)
              raise 'Time logging failed'
            end
          end
        end
        data_array = []
        doc_info = get_document_wide_info
        pdata.each do |index, rec_hash|
          rec_hash.update(doc_info)
          data_array[index]=rec_hash
        end
        return data_array
      end
      
      def record_array_to_pdata_with_filter(pdata, rec, page, cnt, options={})
        opts = {:filter => [], :to_title_column => nil, :calc_column => [], :calc_type => nil, :alias => {}}.merge(options)
        calc_column = [opts[:calc_column]].flatten
        rec.each do |k, v|
          unless opts[:filter].include?(k)
            next
          end
          if opts[:to_title_column] == k
            column_name = @total_title_column
          else
            column_name = opts[:alias].include?(k) ? opts[:alias][k] : k 
          end
          set_value_to_pdata(pdata, page, cnt, column_name, v, rec)
          
          if calc_column.include?(k)
            @calc_remain += (opts[:calc_type] == :add) ? v.to_i : (v.to_i * -1)
            set_value_to_pdata(pdata, page, cnt, @remain_column_name, @calc_remain)
          end
          #to_title_columnなどで置換される前のカラム名で積算されることに注意
          if @total_columns.include?(k)
            @sub_totals ||= {}
            accumulate_vals(@sub_totals, k, v)
          end
        end
        return count_to_next_record_with_opts(pdata, page, cnt)
      end
      
      def conv_date(target_date)
        if target_date.is_a?(Date)
          td = target_date
        else
          td = Date.parse(target_date)
        end
        return td
      end
      
      #税計算方法が請求合計の場合は係数を設定
      #この関数は使わない。
      def set_req_total_duty_calc_weight(company_ar)
        if company_ar and company_ar.duty_calc_type_code == MCODE_CALC_DUTY_REQ_TOTAL
          @req_total_duty_calc_weight = 0.01 * company_ar.duty_rate
        else
          @req_total_duty_calc_weight = nil
        end
      end
      
      def format_value(key, val, rec={})
        return '' unless val
        if @format_price.include?(key)
          if @allow_fraction && @allow_fraction.include?(key)
            str_val = val.to_f % 1 > 0 ? sprintf("%.3f", val) : sprintf("%.0f",val)
          else
            str_val = val.to_i.to_s
          end
          #return str_val.reverse.gsub(/(\d{3})(?=\d)/, '\1,').reverse
          return str_val.gsub(@regex_3digit_comma, '\1,')
        end
        if @format_date.include?(key)
          if val.is_a?(Date)
            val = val.strftime("%m / %d")
          #elsif val.is_a?(String) && val =~ /^\d{4}-\d{2}-\d{2}$/
          elsif val.is_a?(String) && val =~ @regex_date_str
            val = Time.parse(val).strftime("%m / %d")
          end
          return val
        end
        if key == STR_UBD_LABEL && val == STR_NON_ARG_UBD_DB
          return STR_NON_ARG_UBD_DISP
        end
        return val
      end
    end
  end
end
