|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace common\logic\Distribution\Distribution\Service; |
|
|
namespace common\logic\Distribution\Distribution\Service; |
|
|
|
|
|
|
|
|
|
|
|
use common\helpers\Price; |
|
|
use common\logic\AbstractGenerator; |
|
|
use common\logic\AbstractGenerator; |
|
|
use common\logic\Distribution\Distribution\Model\Distribution; |
|
|
use common\logic\Distribution\Distribution\Model\Distribution; |
|
|
use common\logic\Distribution\ProductDistribution\Repository\ProductDistributionRepository; |
|
|
use common\logic\Distribution\ProductDistribution\Repository\ProductDistributionRepository; |
|
|
|
|
|
use common\logic\Document\Document\Service\DocumentSolver; |
|
|
|
|
|
use common\logic\Order\Order\Model\Order; |
|
|
use common\logic\Order\Order\Repository\OrderRepository; |
|
|
use common\logic\Order\Order\Repository\OrderRepository; |
|
|
use common\logic\Order\Order\Service\OrderBuilder; |
|
|
use common\logic\Order\Order\Service\OrderBuilder; |
|
|
|
|
|
use common\logic\Order\Order\Service\OrderSolver; |
|
|
|
|
|
use common\logic\PointSale\PointSale\Model\PointSale; |
|
|
use common\logic\PointSale\PointSale\Repository\PointSaleRepository; |
|
|
use common\logic\PointSale\PointSale\Repository\PointSaleRepository; |
|
|
|
|
|
use common\logic\Producer\Producer\Model\Producer; |
|
|
|
|
|
use common\logic\Product\Product\Model\Product; |
|
|
use common\logic\Product\Product\Repository\ProductRepository; |
|
|
use common\logic\Product\Product\Repository\ProductRepository; |
|
|
|
|
|
use common\logic\Product\Product\Service\ProductSolver; |
|
|
|
|
|
use common\logic\User\UserProducer\Model\UserProducer; |
|
|
use kartik\mpdf\Pdf; |
|
|
use kartik\mpdf\Pdf; |
|
|
|
|
|
|
|
|
class DistributionReportPdfGenerator extends AbstractGenerator |
|
|
class DistributionReportPdfGenerator extends AbstractGenerator |
|
|
{ |
|
|
{ |
|
|
|
|
|
const LIMIT_PRODUCTS = 100; |
|
|
|
|
|
|
|
|
protected OrderRepository $orderRepository; |
|
|
protected OrderRepository $orderRepository; |
|
|
protected OrderBuilder $orderBuilder; |
|
|
protected OrderBuilder $orderBuilder; |
|
|
protected ProductRepository $productRepository; |
|
|
protected ProductRepository $productRepository; |
|
|
protected ProductDistributionRepository $productDistributionRepository; |
|
|
protected ProductDistributionRepository $productDistributionRepository; |
|
|
protected PointSaleRepository $pointSaleRepository; |
|
|
protected PointSaleRepository $pointSaleRepository; |
|
|
|
|
|
protected OrderSolver $orderSolver; |
|
|
|
|
|
protected ProductSolver $productSolver; |
|
|
|
|
|
protected DocumentSolver $documentSolver; |
|
|
|
|
|
|
|
|
public function loadDependencies(): void |
|
|
public function loadDependencies(): void |
|
|
{ |
|
|
{ |
|
|
|
|
|
|
|
|
$this->productRepository = $this->loadService(ProductRepository::class); |
|
|
$this->productRepository = $this->loadService(ProductRepository::class); |
|
|
$this->productDistributionRepository = $this->loadService(ProductDistributionRepository::class); |
|
|
$this->productDistributionRepository = $this->loadService(ProductDistributionRepository::class); |
|
|
$this->pointSaleRepository = $this->loadService(PointSaleRepository::class); |
|
|
$this->pointSaleRepository = $this->loadService(PointSaleRepository::class); |
|
|
|
|
|
$this->orderSolver = $this->loadService(OrderSolver::class); |
|
|
|
|
|
$this->productSolver = $this->loadService(ProductSolver::class); |
|
|
|
|
|
$this->documentSolver = $this->loadService(DocumentSolver::class); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public function generateDistributionReportPdf(Distribution $distribution, bool $save = false) |
|
|
public function generateDistributionReportPdf(Distribution $distribution, bool $save = false) |
|
|
|
|
|
|
|
|
foreach ($pointsSaleArray as $pointSale) { |
|
|
foreach ($pointsSaleArray as $pointSale) { |
|
|
$this->orderBuilder->initPointSaleOrders($pointSale, $ordersArray); |
|
|
$this->orderBuilder->initPointSaleOrders($pointSale, $ordersArray); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
$viewPdf = '@backend/views/distribution/report'; |
|
|
|
|
|
$orientationPdf = Pdf::ORIENT_PORTRAIT; |
|
|
|
|
|
|
|
|
$isBig = count($productsArray) > self::LIMIT_PRODUCTS; |
|
|
|
|
|
|
|
|
if ($producer->slug == 'bourlinguepacotille') { |
|
|
if ($producer->slug == 'bourlinguepacotille') { |
|
|
$viewPdf = '@backend/views/distribution/report-bourlingue'; |
|
|
$viewPdf = '@backend/views/distribution/report-bourlingue'; |
|
|
$orientationPdf = Pdf::ORIENT_LANDSCAPE; |
|
|
$orientationPdf = Pdf::ORIENT_LANDSCAPE; |
|
|
|
|
|
$content = \Yii::$app->getView()->render($viewPdf, [ |
|
|
|
|
|
'date' => $distribution->date, |
|
|
|
|
|
'distribution' => $distribution, |
|
|
|
|
|
'selectedProductsArray' => $selectedProductsArray, |
|
|
|
|
|
'pointsSaleArray' => $pointsSaleArray, |
|
|
|
|
|
'productsArray' => $productsArray, |
|
|
|
|
|
'ordersArray' => $ordersArray, |
|
|
|
|
|
'producer' => $producer |
|
|
|
|
|
]); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// get your HTML raw content without any layouts or scripts |
|
|
|
|
|
$content = \Yii::$app->getView()->render($viewPdf, [ |
|
|
|
|
|
'date' => $distribution->date, |
|
|
|
|
|
'distribution' => $distribution, |
|
|
|
|
|
'selectedProductsArray' => $selectedProductsArray, |
|
|
|
|
|
'pointsSaleArray' => $pointsSaleArray, |
|
|
|
|
|
'productsArray' => $productsArray, |
|
|
|
|
|
'ordersArray' => $ordersArray, |
|
|
|
|
|
'producer' => $producer |
|
|
|
|
|
]); |
|
|
|
|
|
|
|
|
|
|
|
$dateStr = date('d/m/Y', strtotime($distribution->date)); |
|
|
|
|
|
|
|
|
|
|
|
if ($save) { |
|
|
|
|
|
$destination = Pdf::DEST_FILE; |
|
|
|
|
|
} else { |
|
|
|
|
|
$destination = Pdf::DEST_BROWSER; |
|
|
|
|
|
|
|
|
else { |
|
|
|
|
|
$orientationPdf = Pdf::ORIENT_PORTRAIT; |
|
|
|
|
|
$content = $this->buildReportHtml($pointsSaleArray, $productsArray, $ordersArray, $isBig); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
$pdf = new Pdf([ |
|
|
$pdf = new Pdf([ |
|
|
'mode' => Pdf::MODE_UTF8, |
|
|
'mode' => Pdf::MODE_UTF8, |
|
|
'format' => Pdf::FORMAT_A4, |
|
|
'format' => Pdf::FORMAT_A4, |
|
|
'orientation' => $orientationPdf, |
|
|
'orientation' => $orientationPdf, |
|
|
'destination' => $destination, |
|
|
|
|
|
|
|
|
'destination' => $save ? Pdf::DEST_FILE : Pdf::DEST_BROWSER, |
|
|
'filename' => \Yii::getAlias( |
|
|
'filename' => \Yii::getAlias( |
|
|
'@app/web/pdf/Commandes-' . $distribution->date . '-' . $this->getProducerContextId() . '.pdf' |
|
|
'@app/web/pdf/Commandes-' . $distribution->date . '-' . $this->getProducerContextId() . '.pdf' |
|
|
), |
|
|
), |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
', |
|
|
', |
|
|
'methods' => [ |
|
|
'methods' => [ |
|
|
'SetHeader' => ['Commandes du ' . $dateStr], |
|
|
|
|
|
|
|
|
'SetHeader' => ['Commandes du ' . date('d/m/Y', strtotime($distribution->date))], |
|
|
'SetFooter' => ['{PAGENO}'], |
|
|
'SetFooter' => ['{PAGENO}'], |
|
|
] |
|
|
] |
|
|
]); |
|
|
]); |
|
|
|
|
|
|
|
|
return $pdf->render(); |
|
|
return $pdf->render(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function buildReportHtml(array $pointsSaleArray, array $productsArray, array $ordersArray, bool $isBig): string |
|
|
|
|
|
{ |
|
|
|
|
|
$html = $this->buildReportPointsSaleHtml($pointsSaleArray, $productsArray, $isBig); |
|
|
|
|
|
$html .= $this->buildReportPointsSaleTotalHtml($pointsSaleArray, $productsArray, $ordersArray, $isBig); |
|
|
|
|
|
return $html; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function buildReportPointsSaleHtml(array $pointsSaleArray, array $productsArray, bool $isBig): string |
|
|
|
|
|
{ |
|
|
|
|
|
$producer = $this->getProducerContext(); |
|
|
|
|
|
$html = '' ; |
|
|
|
|
|
foreach ($pointsSaleArray as $pointSale) { |
|
|
|
|
|
if (count($pointSale->orders)) { |
|
|
|
|
|
|
|
|
|
|
|
$html .= '<h3>'.$pointSale->name.'</h3>' ; |
|
|
|
|
|
$columnDeliveryNote = ($producer->option_export_display_column_delivery_note) ? '<th>BL</th>' : ''; |
|
|
|
|
|
$colCredit = ($pointSale->credit) ? '<th>Crédit</th>' : '' ; |
|
|
|
|
|
|
|
|
|
|
|
$html .= '<table class="">' |
|
|
|
|
|
. '<thead>' |
|
|
|
|
|
. '<tr>' |
|
|
|
|
|
. '<th>Client</th>' |
|
|
|
|
|
. '<th>Produits</th>' |
|
|
|
|
|
. ($isBig ? '<th>Produits</th>' : '') |
|
|
|
|
|
. '<th>Commentaire</th>' |
|
|
|
|
|
. $columnDeliveryNote |
|
|
|
|
|
. $colCredit |
|
|
|
|
|
. '<th>Montant</th>' |
|
|
|
|
|
. '</tr>' |
|
|
|
|
|
. '</thead>' |
|
|
|
|
|
. '<tbody>'; |
|
|
|
|
|
|
|
|
|
|
|
foreach ($pointSale->orders as $order) { |
|
|
|
|
|
$html .= '<tr>' ; |
|
|
|
|
|
$html .= $this->columnUsername($order); |
|
|
|
|
|
$html .= $this->columnOrderProducts($order, $productsArray); |
|
|
|
|
|
$html .= $this->columnIfIsBig($isBig); |
|
|
|
|
|
$html .= $this->columnComment($order); |
|
|
|
|
|
$html .= $this->columnDeliveryNote($order); |
|
|
|
|
|
$html .= $this->columnCredit($order, $pointSale); |
|
|
|
|
|
$html .= $this->columnOrderAmount($order); |
|
|
|
|
|
$html .= '</tr>' ; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
$html .= '<tr><td><strong>Total</strong></td>' ; |
|
|
|
|
|
|
|
|
|
|
|
$strProducts = ''; |
|
|
|
|
|
$cpt = 0 ; |
|
|
|
|
|
foreach ($productsArray as $product) { |
|
|
|
|
|
foreach( Product::$unitsArray as $unit => $dataUnit) { |
|
|
|
|
|
$quantity = $this->orderSolver->getProductQuantity($product, $pointSale->orders, false, $unit); |
|
|
|
|
|
if ($quantity) { |
|
|
|
|
|
$theUnit = ( $this->productSolver->strUnit($unit, 'wording_short', true) == 'p.') ? '' : ' '. $this->productSolver->strUnit($unit, 'wording_short', true) ; |
|
|
|
|
|
$strProducts .= '(' .$quantity .$theUnit.') '.$this->productSolver->getNameExport($product) . '<br />'; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if($isBig && $cpt == self::LIMIT_PRODUCTS) { |
|
|
|
|
|
$strProducts .= '</td><td>' ; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
$cpt ++ ; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
$html .= '<td>'.$strProducts.'</td><td></td>' ; |
|
|
|
|
|
if($pointSale->credit) { |
|
|
|
|
|
$html .= '<td></td>' ; |
|
|
|
|
|
} |
|
|
|
|
|
if($producer->option_export_display_column_delivery_note) { |
|
|
|
|
|
$html .= '<td></td>' ; |
|
|
|
|
|
} |
|
|
|
|
|
$html .= '<td><strong>'.Price::format($pointSale->revenues_with_tax) . '</strong></td>'; |
|
|
|
|
|
|
|
|
|
|
|
$html .= '</tbody></table><pagebreak>' ; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return $html; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function columnUsername(Order $order): string |
|
|
|
|
|
{ |
|
|
|
|
|
$producer = $this->getProducerContext(); |
|
|
|
|
|
|
|
|
|
|
|
$strUser = $this->orderSolver->getOrderUsername($order) ; |
|
|
|
|
|
if(strlen($order->comment_point_sale)) |
|
|
|
|
|
{ |
|
|
|
|
|
$strUser .= '<br /><em>'.$order->comment_point_sale.'</em>' ; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (isset($order->user) && strlen($order->user->phone)) { |
|
|
|
|
|
$strUser .= '<br />' . $order->user->phone . ''; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if($producer->option_order_reference_type == Producer::ORDER_REFERENCE_TYPE_YEARLY && $order->reference && strlen($order->reference) > 0) { |
|
|
|
|
|
$strUser .= '<br />'.$order->reference ; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return '<td>'.$strUser.'</td>'; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function columnOrderProducts(Order $order, array $productsArray): string |
|
|
|
|
|
{ |
|
|
|
|
|
$strProducts = ''; |
|
|
|
|
|
foreach ($productsArray as $product) { |
|
|
|
|
|
foreach ($order->productOrder as $productOrder) { |
|
|
|
|
|
if($product->id == $productOrder->id_product) { |
|
|
|
|
|
$unit = ( $this->productSolver->strUnit($productOrder->unit, 'wording_short', true) == 'p.') ? '' : ' '. $this->productSolver->strUnit($productOrder->unit, 'wording_short', true) ; |
|
|
|
|
|
$strProducts .= '('.$productOrder->quantity .$unit.') '.$this->productSolver->getNameExport($product) . '<br />'; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return '<td>'.substr($strProducts, 0, strlen($strProducts) - 6).'</td>'; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function columnIfIsBig(bool $isBig): string |
|
|
|
|
|
{ |
|
|
|
|
|
if($isBig) { |
|
|
|
|
|
return '<td></td>' ; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return ''; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function columnComment(Order $order): string |
|
|
|
|
|
{ |
|
|
|
|
|
return '<td>'.$this->orderSolver->getCommentReport($order).'</td>';; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function columnDeliveryNote(Order $order): string |
|
|
|
|
|
{ |
|
|
|
|
|
$producer = $this->getProducerContext(); |
|
|
|
|
|
if($producer->option_export_display_column_delivery_note) { |
|
|
|
|
|
$contentColumnDeliveryNote = ''; |
|
|
|
|
|
if($order->deliveryNote) { |
|
|
|
|
|
if($this->documentSolver->isStatusDraft($order->deliveryNote)) { |
|
|
|
|
|
$contentColumnDeliveryNote = 'Brouillon'; |
|
|
|
|
|
} |
|
|
|
|
|
elseif($this->documentSolver->isStatusValid($order->deliveryNote)) { |
|
|
|
|
|
$contentColumnDeliveryNote = 'Oui'; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
return '<td>'.$contentColumnDeliveryNote.'</td>'; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return ''; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function columnCredit(Order $order, PointSale $pointSale): string |
|
|
|
|
|
{ |
|
|
|
|
|
if($pointSale->credit) { |
|
|
|
|
|
$credit = '' ; |
|
|
|
|
|
|
|
|
|
|
|
if(isset($order->user) && $order->user->id) { |
|
|
|
|
|
$userProducer = UserProducer::searchOne([ |
|
|
|
|
|
'id_user' => $order->user->id |
|
|
|
|
|
]); |
|
|
|
|
|
|
|
|
|
|
|
if($userProducer) { |
|
|
|
|
|
$credit = number_format($userProducer->credit,2).' €' ; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return '<td>'.$credit.'</td>' ; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return ''; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function columnOrderAmount(Order $order): string |
|
|
|
|
|
{ |
|
|
|
|
|
$html = '<td><strong>'.number_format($order->amount_with_tax, 2) . ' € '; |
|
|
|
|
|
|
|
|
|
|
|
if($this->orderSolver->getPaymentStatus($order) == Order::PAYMENT_PAID) |
|
|
|
|
|
{ |
|
|
|
|
|
$html .= '(débité)' ; |
|
|
|
|
|
} |
|
|
|
|
|
elseif($this->orderSolver->getPaymentStatus($order) == Order::PAYMENT_UNPAID && $this->orderSolver->getOrderAmount($order, Order::AMOUNT_PAID)) |
|
|
|
|
|
{ |
|
|
|
|
|
$html .= '(reste '.$this->orderSolver->getOrderAmount($order, Order::AMOUNT_REMAINING, true).' à débiter)' ; |
|
|
|
|
|
} |
|
|
|
|
|
elseif($this->orderSolver->getPaymentStatus($order) == Order::PAYMENT_SURPLUS) |
|
|
|
|
|
{ |
|
|
|
|
|
$html .= '(surplus : '.$this->orderSolver->getOrderAmount($order, Order::PAYMENT_SURPLUS, true).' à recréditer)' ; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
$html .= '</strong></td>' ; |
|
|
|
|
|
|
|
|
|
|
|
return $html; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function buildReportPointsSaleTotalHtml(array $pointsSaleArray, array $productsArray, array $ordersArray, bool $isBig): string |
|
|
|
|
|
{ |
|
|
|
|
|
$html = ''; |
|
|
|
|
|
$html .= '<h3>Points de vente</h3>' ; |
|
|
|
|
|
$html .= '<table class="">' |
|
|
|
|
|
. '<thead>' |
|
|
|
|
|
. '<tr>' |
|
|
|
|
|
. '<th>Point de vente</th>' |
|
|
|
|
|
. '<th>Produits</th>' |
|
|
|
|
|
. ( $isBig ? '<th>Produits</th>' : '') |
|
|
|
|
|
. '<th>Montant</th>' |
|
|
|
|
|
. '</tr>' |
|
|
|
|
|
. '<tbody>'; |
|
|
|
|
|
|
|
|
|
|
|
$revenues = 0 ; |
|
|
|
|
|
foreach ($pointsSaleArray as $pointSale) |
|
|
|
|
|
{ |
|
|
|
|
|
if (count($pointSale->orders)) |
|
|
|
|
|
{ |
|
|
|
|
|
$html .= '<tr><td>'.$pointSale->name.'</td><td>' ; |
|
|
|
|
|
|
|
|
|
|
|
$cpt = 0 ; |
|
|
|
|
|
foreach ($productsArray as $product) { |
|
|
|
|
|
foreach( Product::$unitsArray as $unit => $dataUnit) { |
|
|
|
|
|
$quantity = $this->orderSolver->getProductQuantity($product, $pointSale->orders, false, $unit); |
|
|
|
|
|
if ($quantity) { |
|
|
|
|
|
$theUnit = ( $this->productSolver->strUnit($unit, 'wording_short', true) == 'p.') ? '' : ' '. $this->productSolver->strUnit($unit, 'wording_short', true) ; |
|
|
|
|
|
$html .= '(' .$quantity .$theUnit.') '.$this->productSolver->getNameExport($product) . '<br />'; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if($isBig && $cpt == self::LIMIT_PRODUCTS) { |
|
|
|
|
|
$html .= '</td><td>' ; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
$cpt ++ ; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
$html .= '</td><td>'.Price::format($pointSale->revenues_with_tax, 2).'</td></tr>' ; |
|
|
|
|
|
$revenues += $pointSale->revenues_with_tax ; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// total |
|
|
|
|
|
$html .= '<tr><td><strong>Total</strong></td><td>' ; |
|
|
|
|
|
|
|
|
|
|
|
$cpt = 0 ; |
|
|
|
|
|
foreach ($productsArray as $product) { |
|
|
|
|
|
foreach( Product::$unitsArray as $unit => $dataUnit) { |
|
|
|
|
|
$quantity = $this->orderSolver->getProductQuantity($product, $ordersArray, false, $unit); |
|
|
|
|
|
if ($quantity) { |
|
|
|
|
|
$theUnit = ( $this->productSolver->strUnit($unit, 'wording_short', true) == 'p.') ? '' : ' '. $this->productSolver->strUnit($unit, 'wording_short', true) ; |
|
|
|
|
|
$html .= '(' .$quantity .$theUnit.') '.$product->name . '<br />'; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
if($isBig && $cpt == self::LIMIT_PRODUCTS) { |
|
|
|
|
|
$html .= '</td><td>' ; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
$cpt ++ ; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
$html .= '</td><td><strong>'.number_format($revenues, 2).' €</strong></td></tr>' ; |
|
|
|
|
|
|
|
|
|
|
|
$html .= '</tbody></table>' ; |
|
|
|
|
|
|
|
|
|
|
|
return $html; |
|
|
|
|
|
} |
|
|
} |
|
|
} |