Yii2 - How to send directly to printer after save

I am working on School Fee Payment Application. i want to achieve the following:

  1. Allow payments of the school fees to be done and save into database.

  2. Immediately after save button is clicked, it should send it as receipt directly to the printer. And also display it in PDF format.

I have successfully done No.1 (Allow payment to be save to database). Where I need help is

  1. Immediately after save button is clicked, it should send it as receipt directly to the printer. And also display it in PDF format.

How do I achieve this? I also want to use mPDF for this.

Controller

    public function actionPayFees($sid, $fcid)
{
    $model = new FeesPaymentTransaction();
$stuData = \app\modules\student\models\StuMaster::findOne($sid);
$FccModel = \app\modules\fees\models\FeesCollectCategory::findOne($fcid);
    if($model->load(Yii::$app->request->post())) {

    $model->created_at = new \yii\db\Expression('NOW()');
    $model->created_by = Yii::$app->getid->getId();
    $model->fees_pay_tran_collect_id = $fcid;
    $model->fees_pay_tran_stu_id = $stuData->stu_master_id;
    $model->fees_pay_tran_batch_id = $stuData->stu_master_batch_id;
    $model->fees_pay_tran_course_id = $stuData->stu_master_course_id;
    $model->fees_pay_tran_section_id = $stuData->stu_master_section_id;

    if(!empty($model->fees_pay_tran_date)) {
	$model->fees_pay_tran_date = Yii::$app->dateformatter->getDateFormat($model->fees_pay_tran_date);
    }
    if(!empty($model->fees_pay_tran_cheque_date)) {
   	 $model->fees_pay_tran_cheque_date = Yii::$app->dateformatter->getDateFormat($model->fees_pay_tran_cheque_date);
    }
    if($model->save() && $model->validate()) 
        	return $this->redirect(['pay-fees', 'sid' => $sid, 'fcid'=>$fcid]);

    } 
return $this->render('create', [
	'model' => $model,
	'stuData' => $stuData,
	'FccModel'=> $FccModel,
]);      
}

Models

    public function attributeLabels()
{
    return [
        'fees_pay_tran_id' => Yii::t('fees', 'Receipt No.'),
        'fees_pay_tran_collect_id' => Yii::t('fees', 'Fees Collect Name'),
        'fees_pay_tran_stu_id' => Yii::t('fees', 'Student Name'),
        'fees_pay_tran_batch_id' => Yii::t('fees', 'Batch Name'),
        'fees_pay_tran_course_id' => Yii::t('fees', 'Course Name'),
        'fees_pay_tran_section_id' => Yii::t('fees', 'Section Name'),
        'fees_pay_tran_mode' => Yii::t('fees', 'Payment Mode'),
        'fees_pay_tran_cheque_no' => Yii::t('fees', 'Cheque No'),
        'fees_pay_tran_cheque_date' => Yii::t('fees', 'Cheque Date'),
        'fees_pay_tran_bank_id' => Yii::t('fees', 'Bank Name'),
        'fees_pay_tran_bank_branch' => Yii::t('fees', 'Bank Branch'),
        'fees_pay_tran_amount' => Yii::t('fees', 'Amount'),
        'fees_pay_tran_date' => Yii::t('fees', 'Payment Date'),
    ];
}

View

    <?php $form = ActiveForm::begin([
		'id'=>'fees-collect-form',
		'errorSummaryCssClass' => 'error-summary text-red',
		'fieldConfig' => [
		    'template' => "{label}{input}{error}",
		]]);
?>
<?= $form->errorSummary($model);?>

<!--If select payment mode cash to show this block-->
<div class="col-xs-12 col-sm-12 col-lg-12 no-padding chk-cash">
	<div class="col-xs-12 col-sm-4 col-lg-4">
		<?= $form->field($model, 'fees_pay_tran_mode')->dropDownList(['1'=>Yii::t('fees', 'Cash'), '2'=>Yii::t('fees', 'Cheque')]);?>
	</div>

	<div class="col-xs-12 col-sm-4 col-lg-4">
                <?= $form->field($model, 'fees_pay_tran_date')->widget(yii\jui\DatePicker::className(),
	    [
		'model'=>$model,
		'attribute'=>'fees_pay_tran_date',
		'clientOptions' =>[
			'dateFormat' => 'dd-mm-yyyy',
			'changeMonth'=> true,
			'yearRange'=>(date('Y')-5).':'.(date('Y')+5),
			'changeYear'=> true,
			'readOnly'=>true,
			'autoSize'=>true,
			'buttonImage'=> Yii::$app->homeUrl."images/calendar.png",
		],
		'options'=>[
			'class'=>'form-control',
			'placeholder' => $model->getAttributeLabel('fees_pay_tran_date')
		 ],
	    ]) ?>
	</div>
	<div class="col-xs-12 col-sm-4 col-lg-4">
		<?= $form->field($model, 'fees_pay_tran_amount')->textInput(['maxlength' => 10, 'placeholder' => $model->getAttributeLabel('fees_pay_tran_amount')]) ?>
	</div>
</div>

<div class="col-xs-12 col-sm-12 col-lg-12 no-padding cash-data">
	<div class="col-xs-12 col-sm-4 col-lg-4">

	</div>
	<div class="col-xs-12 col-sm-4 col-lg-4">

	</div>
	<div class="col-xs-12 col-sm-4 col-lg-4">

	</div>
</div>
<!--End payment mode cash-->

<div class="col-xs-12 col-sm-12 col-lg-12 no-padding cheque-data">
	<div class="col-xs-12 col-sm-4 col-lg-4">
		<?= $form->field($model, 'fees_pay_tran_cheque_no')->textInput(['placeholder' => $model->getAttributeLabel('fees_pay_tran_cheque_no')]) ?>
	</div>
	<div class="col-xs-12 col-sm-4 col-lg-4">
		<?= $form->field($model, 'fees_pay_tran_bank_id')->dropDownList(ArrayHelper::map(app\modules\fees\models\BankMaster::find()->where(['is_status'=>0])->all(), 'bank_master_id', 'bank_master_name'), ['prompt'=>'Select Bank']) ?>
	</div>
	<div class="col-xs-12 col-sm-4 col-lg-4">
		<?= $form->field($model, 'fees_pay_tran_bank_branch')->textInput(['placeholder' => $model->getAttributeLabel('fees_pay_tran_bank_branch')]) ?>
	</div>
</div>

<div class="col-xs-12 col-sm-12 col-lg-12 no-padding cheque-data">
	<div class="col-xs-12 col-sm-4 col-lg-4">
		<?= $form->field($model, 'fees_pay_tran_cheque_date')->widget(yii\jui\DatePicker::className(),
		[
		'model'=>$model,
		'attribute'=>'fees_pay_tran_cheque_date',
		'clientOptions' =>[
			'dateFormat' => 'dd-mm-yyyy',
			'changeMonth'=> true,
			'yearRange'=>(date('Y')-5).':'.(date('Y')+5),
			'changeYear'=> true,
			'readOnly'=>true,
			'autoSize'=>true,
			'buttonImage'=> Yii::$app->homeUrl."images/calendar.png",
		],
		'options'=>[
			'class'=>'form-control',
			'placeholder' => $model->getAttributeLabel('fees_pay_tran_cheque_date')
		 ],
		]) ?>
	</div>
	<div class="col-xs-12 col-sm-4 col-lg-4"></div>
	<div class="col-xs-12 col-sm-4 col-lg-4"></div>
</div>
<!--End cheque related field-->
<?php if($collectOn) { echo Html::submitButton($model->isNewRecord ? ' '.Yii::t('fees', ' Take Fees') : ' '.Yii::t('fees', 'Update Fees'), ['class' => $model->isNewRecord ? 'btn btn-primary' : 'btn btn-info']); } ?>
<?php if(!$model->isNewRecord) { echo Html::a(Yii::t('fees', 'Cancel'),['pay-fees', 'sid'=>$stuData->stu_master_id, 'fcid'=>$FccModel->fees_collect_category_id], ['class' => 'btn btn-default']); } ?>
</div>
<?php ActiveForm::end(); ?>

How do I achieve this? I also want to use mPDF for this.

use CUPS’s “lpr” (https://www.cups.org/doc/man-lpr.html) command.

1.) Install CUPS on Server (https://wiki.debian.org/SystemPrinting, https://en.wikipedia.org/wiki/CUPS) and Add the printer.

2.) save the PDF to Filesystem:

$mpdf->Output('filename.pdf', \Mpdf\Output\Destination::FILE);

3.) schedule the print-job (non blocking i/o)

exec('lpr -P printerName "filename.pdf" > /dev/null 2>&1 &');

CUPS:
display all printer and Callable printerNames:

lpstat -s // with sudo or as root
1 Like

I need a sample code of how to integrate into my existing code. As soon as I save (save button) into the database through my Yii2 code, the receipt will be printed in the POS (thermal) printer for the student/customer. Thanks

/** code is not tested. **/

Install CUPS:

Debian/ubuntu:

sudo apt update && sudo apt install cups cups-client cups-bsd

CentOS

yum install cups*

Windows
Dont work.

<?php

namespace app\modules\fees\controllers;

use Yii;
use app\modules\fees\models\FeesPaymentTransaction;

/**
 * Keep your controllers clean and small!
 * Move all the Business logic to the models
 */
class FeeController extends \yii\web\Controller
{
	
	public function actionPayFees($sid, $fcid)
	{
		/** @var ready configured Model **/
		$objPaymentTransaction = FeesPaymentTransaction::getInstance($sid, $fcid);

		if( 
			$objPaymentTransaction->load(Yii::$app->request->post())
			&& $objPaymentTransaction->save()
		) {
			/**
			 * 1.) yii's AR save()-method validates the model. No need to call it manually.
			 * 2.) transaction-date and tran_cheque_date are setted by Model beforeSave()-method
			 * 3.) created_at/created_by are setted by Models behaviors()
			 * 4.) after the Model is saved, the afterSave()-method is triggered.(the PDF was sent to printer)
			 */
			return $this->redirect(['pay-fees', 'sid' => $sid, 'fcid'=>$fcid]);
		}		

		return $this->render('create', [
			'model' => $model,
			'stuData' => $stuData,
			'FccModel'=> $FccModel,
		]);      
	}
}



<?php

namespace app\modules\fees\models;

use Yii;
use yii\behaviors\TimestampBehavior;
use yii\behaviors\BlameableBehavior;

use app\modules\fees\models\FeesCollectCategory;
use app\modules\student\models\StuMaster;

class FeesPaymentTransaction extends \yii\db\ActiveRecord
{
	/**
	 * to find out type in shell/bash:
	 * #lpstat -s // with sudo or as root
	 * @var string 
	 */
	public $cupsPrinterName = 'myZebraLabelPrinter';

	/** 
	 * @var string Path to store pdfs temporarily 
	 **/
	public $tempPath = '@web/runtime/mpdftemp';

	public static function tabelName()
	{
		return 'foobar';
	}

	/**
	 * @param int $sid Studentid
	 * @param int $fcid Fee collect id
	 *
	 * @return \app\modules\fees\models\FeesPaymentTransaction
	 */
	public static function getInstance(int $sid, int $fcid): FeesPaymentTransaction
	{
		$objStuData  = StuMaster::findOne($sid);
		$objFccModel = FeesCollectCategory::findOne($fcid);

		if (!$objStuData || !$objFccModel) {
			throw new \yii\base\InvalidArgumentException('$sid or $fcid are invalid!');
		}

		/**
		 * Hint: fees_pay_tran_date & fees_pay_tran_cheque_date are
		 * processed in afterSave() method.
		 */
		$objPaymentTransaction = Yii::createObject([
			'class'						=> self::class,
			'fees_pay_tran_collect_id' 	=> $objFccModel->getPrimaryKey(),
			'fees_pay_tran_stu_id' 		=> $objStuData->stu_master_id,
			'fees_pay_tran_batch_id'	=> $objStuData->stu_master_batch_id,
			'fees_pay_tran_course_id'	=> $objStuData->stu_master_course_id,
			'fees_pay_tran_section_id'	=> $objStuData->stu_master_section_id,
		]);

		return $objPaymentTransaction;
	}

	/**
	 * render PDF and send it to CUPS
	 */
	public function printPdf()
	{
		/** 
		 * call your Mpdf Service locator or implementation here
		 * This example assumes, that there is an Servicelocator is 
		 * registered (via config file, "components").
		 * 
		 * I recommend to implement a method "renderByModel" which returns
		 * the ready rendered Pdf.
		 **/
		$pdf = Yii::$app->mpdf->renderByModel($this);

		$path = Yii::getAlias($this->tempPath);
		$fileName = Yii::$app->security->generateRandomString() . '.pdf';
		$fileLocation = $path . DIRECTORY_SEPARATOR . $fileName;

		if (!is_dir($path)) {
			\yii\helpers\FileHelper::createDirectory($path);
		}

		$pdf->Output($fileLocation, \Mpdf\Output\Destination::FILE);

		exec('lpr -P ' . $this->cupsPrinterName . ' "' . $fileLocation . '" > /dev/null 2>&1 &');
	}

	/**
	 * process dateformat of fees_pay_tran_date & fees_pay_tran_cheque_date
	 * {@inheritdoc}
	 */
	public function beforeSave($insert)
	{
		if ($this->fees_pay_tran_date) {
			$this->fees_pay_tran_date = Yii::$app->dateformatter->getDateFormat(
				$model->fees_pay_tran_date
			);
		}

		if ($this->fees_pay_tran_cheque_date) {
			$this->fees_pay_tran_cheque_date = Yii::$app->dateformatter->getDateFormat(
				$model->fees_pay_tran_cheque_date
			);
		}

		parent::beforeSave($insert);
	}

	public function afterSave()
	{
		$this->printPdf();
		return parent::afterSave();
	}

    /**
     * NOTE: is you dont want the current user's Primary Key stored
     * to created_by/updated_by, remove blameable
     *  
     * @inheritdoc 
     **/
	public function behaviors()
	{
	    return [
            'tstamp' => [
                'class' => TimestampBehavior::class,
                'value' => (new \yii\db\Expression('NOW()'))
            ],
            'blameable' => [
                'class' => BlameableBehavior::class,
            ]	        
	    ];
	}
}

Not resolved

@HenryVolkmer,
Probably @folumike wants to use the client’s printer instead of the one installed on the server.
I guess we need other approach.

i assume the Server processes the PHP-Application. If the client-printer is not connected to to the network then the plan is not doable.

If the client-printer is connected to the Network, @folumike could provide all printers via CUPS and install the “CUPS-Network-Printer” on all clients. At next, each User (Identity) Model get a new attribute “user_printer_name” (text). Simply store the clients Printer name (lpstat -s) in this DB-Field.

Finally, replace

$this->cupsPrinterName

with

Yii::$app->user->identity->user_printer_name
1 Like

Yes. I want to use client’s printer

@folumike,
As @HenryVolkmer says, his solution will work when you are running your application on a linux server within a closed network in which all your client PCs and network printers are located.
So, could you tell us more detailed specs of your production environment?