Merge pull request #100 from apuc/document

Document
This commit is contained in:
2022-11-21 14:05:44 +03:00
committed by GitHub
76 changed files with 4360 additions and 2382 deletions

View File

@@ -1,65 +0,0 @@
<?php
namespace common\models;
use Yii;
/**
* This is the model class for table "accompanying_document".
*
* @property int $id
* @property int $document_id
* @property string $title
*
* @property Document $document
*/
class AccompanyingDocument extends \yii\db\ActiveRecord
{
public $accompanying_document;
/**
* {@inheritdoc}
*/
public static function tableName()
{
return 'accompanying_document';
}
/**
* {@inheritdoc}
*/
public function rules()
{
return [
[['document_id'], 'integer'],
[['title'], 'required'],
[['title'], 'string', 'max' => 255],
[['template_file_name', 'title'], 'required'],
[['accompanying_document'], 'required', 'message'=>'Укажите путь к файлу'],
[['accompanying_document'], 'file', 'maxSize' => '100000'],
[['accompanying_document'], 'file', 'skipOnEmpty' => true, 'extensions' => 'docx'],
[['document_id'], 'exist', 'skipOnError' => true, 'targetClass' => Document::className(), 'targetAttribute' => ['document_id' => 'id']],
];
}
/**
* {@inheritdoc}
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'document_id' => 'Документ',
'title' => 'Название',
'accompanying_document' => 'Сопроводительный документ'
];
}
/**
* @return \yii\db\ActiveQuery
*/
public function getDocument()
{
return $this->hasOne(Document::className(), ['id' => 'document_id']);
}
}

View File

@@ -2,28 +2,34 @@
namespace common\models;
use Yii;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveQuery;
use yii\db\Expression;
use yii\db\StaleObjectException;
/**
* This is the model class for table "document".
*
* @property int $id
* @property int $company_id
* @property int $contractor_company_id
* @property int $manager_id
* @property int $contractor_manager_id
* @property int $template_id
* @property string $title
* @property string $body
* @property string $created_at
* @property string $updated_at
* @property int $template_id
* @property int $manager_id
*
* @property Company $company
* @property Company $contractorCompany
* @property Manager $contractorManager
* @property DocumentTemplate $template
* @property Manager $manager
* @property Template $template
* @property DocumentFieldValue[] $documentFieldValues
*/
class Document extends \yii\db\ActiveRecord
{
const SCENARIO_UPDATE_DOCUMENT_BODY = 'update_document_body';
const SCENARIO_DOWNLOAD_DOCUMENT = 'download_document';
/**
* {@inheritdoc}
*/
@@ -44,31 +50,30 @@ class Document extends \yii\db\ActiveRecord
];
}
/**
* @throws \Throwable
* @throws StaleObjectException
*/
public function beforeDelete()
{
foreach ($this->documentFieldValues as $documentFieldValue){
$documentFieldValue->delete();
}
return parent::beforeDelete();
}
/**
* {@inheritdoc}
*/
public function rules()
{
return [
[['company_id', 'contractor_company_id', 'manager_id', 'contractor_manager_id', 'title', 'template_id'], 'required'],
[['company_id', 'contractor_company_id', 'manager_id', 'contractor_manager_id', 'template_id'], 'integer'],
[['body'], 'string'],
[['created_at', 'updated_at'], 'safe'],
[['template_id', 'manager_id'], 'required'],
[['template_id', 'manager_id'], 'integer'],
['title', 'unique', 'targetAttribute' => ['title', 'template_id'], 'message'=>'Документ уже создан'],
[['title'], 'string', 'max' => 255],
[['company_id'], 'exist', 'skipOnError' => true, 'targetClass' => Company::className(), 'targetAttribute' => ['company_id' => 'id']],
[['contractor_company_id'], 'exist', 'skipOnError' => true, 'targetClass' => Company::className(), 'targetAttribute' => ['contractor_company_id' => 'id']],
[['contractor_manager_id'], 'exist', 'skipOnError' => true, 'targetClass' => Manager::className(), 'targetAttribute' => ['contractor_manager_id' => 'id']],
[['template_id'], 'exist', 'skipOnError' => true, 'targetClass' => DocumentTemplate::className(), 'targetAttribute' => ['template_id' => 'id']],
[['manager_id'], 'exist', 'skipOnError' => true, 'targetClass' => Manager::className(), 'targetAttribute' => ['manager_id' => 'id']],
[['template_id'], 'exist', 'skipOnError' => true, 'targetClass' => Template::className(), 'targetAttribute' => ['template_id' => 'id']],
['body', 'required', 'on' => self::SCENARIO_UPDATE_DOCUMENT_BODY],
['body', function ($attribute, $params) {
preg_match_all('/(\${\w+})/', $this->$attribute,$out);
if (!empty($out[0])) {
$this->addError('body', 'В теле документа все переменные должны бвть заменены!');
}
}, 'on' => self::SCENARIO_DOWNLOAD_DOCUMENT
],
];
}
@@ -79,35 +84,55 @@ class Document extends \yii\db\ActiveRecord
{
return [
'id' => 'ID',
'title' => 'Название',
'created_at' => 'Дата создания',
'updated_at' => 'Дата обновления',
'template_id' => 'Шаблон',
'company_id' => 'Компания',
'contractor_company_id' => 'Компания контрагент',
'manager_id' => 'Менеджер',
'contractor_manager_id' => 'Менеджер контрагент',
'template_id' => 'Шаблон документа',
'title' => 'Название',
'body' => 'Тело документа',
'created_at' => 'Created At',
'updated_at' => 'Updated At',
];
}
/**
* @return ActiveQuery
* @return \yii\db\ActiveQuery
*/
public function getTemplate()
{
return $this->hasOne(DocumentTemplate::className(), ['id' => 'template_id']);
}
/**
* @return \yii\db\ActiveQuery
*/
public function getCompany()
{
return $this->hasOne(Company::className(), ['id' => 'company_id']);
}
/**
* @return \yii\db\ActiveQuery
*/
public function getContractorCompany()
{
return $this->hasOne(Company::className(), ['id' => 'contractor_company_id']);
}
/**
* @return \yii\db\ActiveQuery
*/
public function getContractorManager()
{
return $this->hasOne(Manager::className(), ['id' => 'contractor_manager_id']);
}
/**
* @return \yii\db\ActiveQuery
*/
public function getManager()
{
return $this->hasOne(Manager::className(), ['id' => 'manager_id']);
}
/**
* @return ActiveQuery
*/
public function getTemplate()
{
return $this->hasOne(Template::className(), ['id' => 'template_id']);
}
/**
* @return ActiveQuery
*/
public function getDocumentFieldValues(): ActiveQuery
{
return $this->hasMany(DocumentFieldValue::className(), ['document_id' => 'id']);
}
}

View File

@@ -2,7 +2,6 @@
namespace common\models;
use common\helpers\TransliteratorHelper;
use Yii;
use yii\helpers\ArrayHelper;
@@ -12,9 +11,6 @@ use yii\helpers\ArrayHelper;
* @property int $id
* @property string $title
* @property string $field_template
*
* @property DocumentFieldValue[] $documentFieldValues
* @property TemplateDocumentField[] $templateDocumentFields
*/
class DocumentField extends \yii\db\ActiveRecord
{
@@ -36,12 +32,6 @@ class DocumentField extends \yii\db\ActiveRecord
];
}
public function beforeSave($insert)
{
$this->field_template = TransliteratorHelper::transliterate($this->title);
return true;
}
/**
* {@inheritdoc}
*/
@@ -54,27 +44,8 @@ class DocumentField extends \yii\db\ActiveRecord
];
}
/**
* @return \yii\db\ActiveQuery
*/
public function getDocumentFieldValues()
public static function getTitleFieldTemplateArr(): array
{
return $this->hasMany(DocumentFieldValue::className(), ['field_id' => 'id']);
}
/**
* @return \yii\db\ActiveQuery
*/
public function getTemplateDocumentFields()
{
return $this->hasMany(TemplateDocumentField::className(), ['field_id' => 'id']);
}
public static function getIdFieldsTitleList($template_id): array
{
return
self::find()->joinWith('templateDocumentFields')
->where(['template_document_field.template_id' => $template_id])
->asArray()->all();
return ArrayHelper::map(self::find()->all(), 'title', 'field_template');
}
}

View File

@@ -1,72 +0,0 @@
<?php
namespace common\models;
use Yii;
use yii\db\ActiveQuery;
/**
* This is the model class for table "document_field_value".
*
* @property int $id
* @property int $field_id
* @property int $document_id
* @property string $value
*
* @property Document $document
* @property DocumentField $field
*/
class DocumentFieldValue extends \yii\db\ActiveRecord
{
/**
* {@inheritdoc}
*/
public static function tableName()
{
return 'document_field_value';
}
/**
* {@inheritdoc}
*/
public function rules()
{
return [
[['field_id', 'document_id', 'value'], 'required'],
[['field_id', 'document_id'], 'integer'],
[['value'], 'string', 'max' => 255],
['field_id', 'unique', 'targetAttribute' => ['field_id', 'document_id'], 'message'=>'Поле уже используется'],
[['document_id'], 'exist', 'skipOnError' => true, 'targetClass' => Document::className(), 'targetAttribute' => ['document_id' => 'id']],
[['field_id'], 'exist', 'skipOnError' => true, 'targetClass' => DocumentField::className(), 'targetAttribute' => ['field_id' => 'id']],
];
}
/**
* {@inheritdoc}
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'field_id' => 'Поле',
'document_id' => 'Документ',
'value' => 'Значение',
];
}
/**
* @return ActiveQuery
*/
public function getDocument()
{
return $this->hasOne(Document::className(), ['id' => 'document_id']);
}
/**
* @return ActiveQuery
*/
public function getField(): ActiveQuery
{
return $this->hasOne(DocumentField::className(), ['id' => 'field_id']);
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace common\models;
use Yii;
use yii\behaviors\TimestampBehavior;
use yii\db\Expression;
/**
* This is the model class for table "document_template".
*
* @property int $id
* @property string $title
* @property string $template_body
* @property int $status
* @property string $created_at
* @property string $updated_at
*/
class DocumentTemplate extends \yii\db\ActiveRecord
{
/**
* {@inheritdoc}
*/
public static function tableName()
{
return 'document_template';
}
public function behaviors()
{
return [
[
'class' => TimestampBehavior::class,
'createdAtAttribute' => 'created_at',
'updatedAtAttribute' => 'updated_at',
'value' => new Expression('NOW()'),
],
];
}
/**
* {@inheritdoc}
*/
public function rules()
{
return [
[['status', 'title', 'template_body'], 'required'],
[['template_body'], 'string'],
[['status'], 'integer'],
[['created_at', 'updated_at'], 'safe'],
[['title'], 'string', 'max' => 255],
];
}
/**
* {@inheritdoc}
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'title' => 'Название',
'template_body' => 'Тело шаблона',
'status' => 'Статус',
'created_at' => 'Created At',
'updated_at' => 'Updated At',
];
}
}

View File

@@ -1,132 +0,0 @@
<?php
namespace common\models;
use Yii;
use yii\base\InvalidConfigException;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveQuery;
use yii\db\Expression;
/**
* This is the model class for table "template".
*
* @property int $id
* @property string $title
* @property string $created_at
* @property string $updated_at
* @property string $template_file_name
* @property int $document_type
*
* @property Document[] $documents
* @property TemplateDocumentField[] $templateDocumentFields
*/
class Template extends \yii\db\ActiveRecord
{
const SCENARIO_UPDATE_TITLE = 'update';
const SCENARIO_UPDATE_FILE = 'update';
public $template;
/**
* {@inheritdoc}
*/
public static function tableName()
{
return 'template';
}
public function behaviors()
{
return [
[
'class' => TimestampBehavior::class,
'createdAtAttribute' => 'created_at',
'updatedAtAttribute' => 'updated_at',
'value' => new Expression('NOW()'),
],
];
}
/**
* {@inheritdoc}
*/
public function rules()
{
return [
[['created_at', 'updated_at'], 'safe'],
[['title'], 'unique'],
[['document_type'], 'integer'],
[['template_file_name', 'title', 'document_type'], 'required'],
[['template'], 'required', 'message'=>'Укажите путь к файлу'],
[['template'], 'file', 'maxSize' => '100000'],
[['template'], 'file', 'skipOnEmpty' => true, 'extensions' => 'docx'],
[['title', 'template_file_name'], 'string', 'max' => 255],
];
}
public function scenarios()
{
$scenarios = parent::scenarios();
$scenarios[static::SCENARIO_UPDATE_TITLE] = ['created_at', 'updated_at', 'title', 'template_file_name'];
$scenarios[static::SCENARIO_UPDATE_FILE] = ['template'];
return $scenarios;
}
/**
* {@inheritdoc}
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'title' => 'Название',
'created_at' => 'Дата создания',
'updated_at' => 'Дата изменения',
'template_file_name' => 'Файл шаблона',
'document_type' => 'Тип документа',
];
}
public function beforeDelete()
{
foreach ($this->templateDocumentFields as $templateDocumentField){
$templateDocumentField->delete();
}
if (!empty($this->template_file_name)) {
$template_path = Yii::getAlias('@templates') . '/' . $this->template_file_name;
if(file_exists($template_path)) {
unlink($template_path);
}
}
return parent::beforeDelete();
}
/**
* @return ActiveQuery
*/
public function getDocuments()
{
return $this->hasMany(Document::className(), ['template_id' => 'id']);
}
/**
* @return ActiveQuery
*/
public function getTemplateDocumentFields()
{
return $this->hasMany(TemplateDocumentField::className(), ['template_id' => 'id']);
}
public function getTitle()
{
return $this->title;
}
public function getFields()
{
return $this->hasMany(DocumentField::className(), ['id' => 'field_id'])
->via('templateDocumentFields');
}
}

View File

@@ -1,69 +0,0 @@
<?php
namespace common\models;
use Yii;
use yii\helpers\ArrayHelper;
/**
* This is the model class for table "template_document_field".
*
* @property int $id
* @property int $template_id
* @property int $field_id
*
* @property DocumentField $field
* @property Template $template
*/
class TemplateDocumentField extends \yii\db\ActiveRecord
{
/**
* {@inheritdoc}
*/
public static function tableName()
{
return 'template_document_field';
}
/**
* {@inheritdoc}
*/
public function rules()
{
return [
[['template_id', 'field_id'], 'required'],
[['template_id', 'field_id'], 'integer'],
['field_id', 'unique', 'targetAttribute' => ['template_id', 'field_id'], 'message'=>'Поле уже назначено'],
[['field_id'], 'exist', 'skipOnError' => true, 'targetClass' => DocumentField::className(), 'targetAttribute' => ['field_id' => 'id']],
[['template_id'], 'exist', 'skipOnError' => true, 'targetClass' => Template::className(), 'targetAttribute' => ['template_id' => 'id']],
];
}
/**
* {@inheritdoc}
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'template_id' => 'Шаблон',
'field_id' => 'Поле',
];
}
/**
* @return \yii\db\ActiveQuery
*/
public function getField()
{
return $this->hasOne(DocumentField::className(), ['id' => 'field_id'])->asArray();
}
/**
* @return \yii\db\ActiveQuery
*/
public function getTemplate()
{
return $this->hasOne(Template::className(), ['id' => 'template_id']);
}
}

View File

@@ -3,6 +3,9 @@
namespace common\services;
use common\models\Document;
use common\models\DocumentTemplate;
use DateTime;
use kartik\mpdf\Pdf;
class DocumentService
{
@@ -25,4 +28,94 @@ class DocumentService
->asArray()->all();
}
public static function getDocumentNumber(): string
{
$documents = Document::find()->where(['DATE(`created_at`)' => date('Y-m-d')])->orderBy('id DESC')->all();
$date = new DateTime();
foreach ($documents as $document) {
preg_match_all('/\b\d{2}\.\d{2}\.\d{4}\/\d{3}\b/', $document->body,$out);
if (!empty($out[0])) {
$num = substr($out[0][0], -3);
$num++;
$num = str_pad($num, 3, "0", STR_PAD_LEFT);
return $date->format('d.m.Y') . '/' . $num;
}
}
return $date->format('d.m.Y') . '/001';
}
public static function downloadPdf($id)
{
$model = Document::findOne($id);
$pdf = new Pdf(); // or new Pdf();
$mpdf = $pdf->api; // fetches mpdf api
$mpdf->SetFooter('{PAGENO}');
$mpdf->WriteHtml($model->body); // call mpdf write html
echo $mpdf->Output("{$model->title}", 'D'); // call the mpdf api output as needed
}
public static function downloadDocx($id)
{
$model = Document::findOne($id);
$pw = new \PhpOffice\PhpWord\PhpWord();
// (B) ADD HTML CONTENT
$section = $pw->addSection();
$documentText = str_replace(array('<br/>', '<br>', '</br>'), ' ', $model->body);
\PhpOffice\PhpWord\Shared\Html::addHtml($section, $documentText, false, false);
// (C) SAVE TO DOCX ON SERVER
// $pw->save("convert.docx", "Word2007");
// (D) OR FORCE DOWNLOAD
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment;filename=\"$model->title.docx\"");
$objWriter = \PhpOffice\PhpWord\IOFactory::createWriter($pw, "Word2007");
$objWriter->save("php://output");
exit();
}
public static function generateDocumentBody(Document $model)
{
$templateModel = DocumentTemplate::findOne($model->template_id);
preg_match_all('/(\${\w+})/', $templateModel->template_body,$out);
$document = $templateModel->template_body;;
foreach ($out[0] as $field) {
if (str_contains($document, $field)) {
switch ($field)
{
case '${contract_number}':
$fieldValue = DocumentService::getDocumentNumber();
break;
case '${title}':
$fieldValue = $model->title;
break;
case '${company}':
$fieldValue = $model->company->name;
break;
case '${manager}':
$fieldValue = $model->manager->userCard->fio;
break;
case '${contractor_company}':
$fieldValue = $model->contractorCompany->name;
break;
case '${contractor_manager}':
$fieldValue = $model->contractorManager->userCard->fio;
break;
default:
$fieldValue = $field;
break;
}
$document = str_replace($field, $fieldValue, $document);
}
}
$model->body = $document;
}
}