S.B.S. Salesforce技術ブログ

教えて921さん!

見積書の改ページ

こんにちは!KMです。

今回は、PDFで作成した見積書の改ページについてお話ししたいと思います。

改ページ

CSSのpage-breakとApexのrepeatを利用することで、ページレイアウトを変更せずにレコードの内容だけを変更させていくことができます。

  • Visualforce page(表示されるページ)
  • コントローラ(Apex class)
  • ラッパー(Apex class)

の3つで設定します。

Visualforce page

ここではページのレイアウトなどを設定します。

ポイントは、Apex repeatを

  • ページ全体に対して
  • レコードのリストに対して

の計2回行う点です。 前者の終了タグの直前にpage-break-afterを設定すると改ページがなされます。

コントローラ

ここでは帳票に必要なレコードの取得と1ページに表示する件数の振り分けを行います。

レコードの振り分けの際はレコードの総数が1ページ当たりの件数で割り切れるかどうかで分岐処理をします。

ラッパー

ここではページのカウントとそのページに入るレコードリストを対応付けするためのコンストラクタを用意します。

やってみましょう

それではやってみましょう! 今回は

  • 商談(Opportunity)
  • 商談商品(negotiation_products__c)

の2つのオブジェクト、
1ページ当たり10件で作成すると仮定します。

コントローラ

イメージとしては、取得したレコードを10件ずつのリストに詰め込んでいく感じです!

public class ctrl_kaiOpportunity {
    private final Opportunity opt;
    //pdf全体
    public List<wrap_Opportunity>PdfPage{get;set;}
    //1ページ
    public List<negotiation_products__c> Neg{get;set;}
    //データ保管用
    public List<negotiation_products__c> NegList{get;set;}
    //コンストラクタ
    public ctrl_kaiOpportunity(ApexPages.StandardController stdController){
        this.opt=(Opportunity)stdController.getRecord();
        Info();
        PDF();
    }
//商談商品のレコード取得
    public void Info(){
        Opportunity o = [select Id From Opportunity Where Id=:opt.Id];
        List<negotiation_products__c>neg = [
                select Id,
                nebiki__c,
                suuryou__c,
                kinngaku__c,
                buisiness_code__c,
                product_name__r.name,
                product_name__r.Sales__c
                From negotiation_products__c
                Where buisiness_code__c=:o.Id];
        system.debug(neg);
        NegList=neg;
        system.debug(NegList);
        system.debug(NegList.size());
    }
    //10件ごとに分ける
    public void PDF(){
        //レコード数
        integer r = Neglist.size();
        //1セットに入る件数
        integer OneSet = 10;
        //セット数
        integer SetCount = r/OneSet;
        //セットされたアイテム数
        integer ItemSetCount;
        system.debug(SetCount);
        wrap_Opportunity PdfLine = new wrap_Opportunity();
        PdfPage =new List<wrap_Opportunity>();
        //割り切れるかどうかの判断を剰余から判別する
        integer Mod = Math.mod(Neglist.size(), OneSet);
        //レコード数が10で割り切れるとき
        if(Mod==0){
        //ページ数で回す
            for (integer i = 0;i<SetCount;i++){
                 r=OneSet*i;
                 ItemSetCount=OneSet*(i+1);
                 system.debug(r);
                system.debug(i);
        //1ページごとのレコードを詰める
                    for (integer j=10*i;j<10*(i+1);j++){
                        PdfLine.Neg.add(NegList[j]);
                    }  
                system.debug(PdfLine);
                PdfPage.add(PdfLine);
                system.debug(PdfPage);
                PdfLine = new wrap_Opportunity();
               } 
            
            //10で割り切れないとき
        }else{
            //ページ数で回す
            for (integer i = 0;i<SetCount;i++){
                r=OneSet*i;
                 ItemSetCount=OneSet*(i+1);
                 system.debug(r);
                //10件以上の時は10件ずつレコードを詰める。
                for (integer j=10*i;j<10*(i+1);j++){
                        PdfLine.Neg.add(NegList[j]);    
                    } 
                integer pageNo =i;
                PdfLine.PageNo.add(pageNo);
                system.debug(PdfLine.PageNo);
                PdfPage.add(PdfLine);
                system.debug(PdfPage);
                PdfLine = new wrap_Opportunity();
                system.debug(ItemSetCount);            
                //端数の詰め込み
                for(integer k=ItemSetCount;k<ItemSetCount+Mod;k++){
                        PdfLine.Neg.add(NegList[k]);
                    }
                    PdfLine.PageNo.add(pageNo+1);
                    system.debug(PdfLine.PageNo);
                system.debug(PdfLine);
                PdfPage.add(PdfLine);
                system.debug(PdfPage);
                PdfLine = new wrap_Opportunity(); 
            }
        }
    }
}

ラッパー

public class wrap_Opportunity {
   public List<negotiation_products__c> Neg{get;set;}
   public List<Integer> PageNo{get;set;}
   public boolean LastPageFlag{get;set;}
      //コンストラクタ
   public wrap_Opportunity(){
       Neg = new List<negotiation_products__c>();
       PageNo = new List<Integer>();
       
   }
}

Visualforce page

レイアウトは割愛させていただきます。
今回はrepeatを2回使うときの雰囲気を感じっとっていただければと思います。

<apex:page renderAs="pdf" applyhtmltag="false" showheader="false"  standardController="Opportunity" extensions="ctrl_kaiOpportunity">
    <head>
        <style>
            @page{
            
            margin: 8mm 10mm 20mm 10mm;
            }            
            *{
            margin: 0;
            padding: 0;
            }
            .clearfix::after {
            content: "";
            display: block;
            clear: both;
            }
            body{
            font-family: Arial Unicode MS;
            font-size: 16px;
            }
            .page-break{
             page-break-after:always;
            }
        </style>
    </head>
   <!--ページに対するリピート-->
    <apex:repeat value="{!PdfPage}" var="PdfLine" >
        <body>
<div style="position:relative;top:30px;">   
                <apex:variable var="rowNum" value="{!1}" />
   <!--1ページ当たりのレコードのリピート-->
                <apex:repeat value="{!PdfLine.Neg}" var="N" >                  
                    <div>
                        <span style="width:50px;line-height:30px;text-align:right;">
                            <apex:outputText >{!rowNum}</apex:outputText>
                        </span>
                        <span style="position:absolute;left:50px;width:300px;line-height:30px;text-align:center;">
                            <apex:outputText value="{!N.product_name__r.name}"/>
                        </span>
                        <span style="position:absolute;left:350px;width:95px;line-height:30px;text-align:center;">
                            <apex:outputText value="{!N.suuryou__c}"/>
                        </span>
                        <span style="position:absolute;left:445px;width:95px;line-height:30px;text-align:center;">
                            <apex:outputText value="{!N.product_name__r.Sales__c}"/>
                        </span>
                        <sapn style="position:absolute;left:540px;width:95px;line-height:30px;text-align:center;">
                            <apex:outputText value="{!N.nebiki__c}"/>
                        </sapn>
                        <span style="position:absolute;left:635px;width:105px;line-height:30px;text-align:right;">
                            ¥<apex:outputText value="{!N.kinngaku__c}"/>
                        </span>
                        <apex:variable var="rowNum" value="{!rowNum + 1}" />
                    </div>
                </apex:repeat>
</div> 
 <div class="page-break"></div>        
        </body>
    </apex:repeat>
</apex:page>

まとめ

いかがでしょうか。
今回の内容は、かなり難しく感じたと思います。

一度にすべてやるのではなく、
今回の例で言うと

  1. 改ページなしの見積書を作成する
  2. ページ数で割り切れるレコード数で改ページする
  3. その他の場合のレコード数で改ページする

といった感じで、一つずつ実装していくのが成功の近道だと思います。