]> gitweb.erp-flowers.ru Git - yii-erp24/.git/commitdiff
правки
authorAleksey Filippov <afilippov@bigland.ru>
Fri, 29 Dec 2023 08:36:45 +0000 (11:36 +0300)
committerAleksey Filippov <afilippov@bigland.ru>
Fri, 29 Dec 2023 08:36:45 +0000 (11:36 +0300)
27 files changed:
docker/php/Dockerfile
erp24/forms/EmployeePaymentSearch.php [new file with mode: 0755]
erp24/forms/MultipleUploadForm.php [new file with mode: 0644]
erp24/forms/UploadImageForm.php [new file with mode: 0644]
erp24/forms/device/AddDeviceForm.php [new file with mode: 0755]
erp24/forms/device/SelectDeviceForm.php [new file with mode: 0755]
erp24/forms/infoTable/ChartManagementForm.php [new file with mode: 0644]
erp24/forms/infoTable/Charts.php [new file with mode: 0644]
erp24/forms/infoTable/YearWeekSearchForm.php [new file with mode: 0644]
erp24/forms/lesson/LessonForm.php [new file with mode: 0755]
erp24/forms/log/ErrorLogSearchModel.php [new file with mode: 0755]
erp24/forms/payroll/YearMonthSearchForm.php [new file with mode: 0755]
erp24/forms/planStore/AddPlanStore.php [new file with mode: 0644]
erp24/forms/support/TaskSupportCreateForm.php [new file with mode: 0755]
erp24/forms/timetable/AddForm.php [new file with mode: 0755]
erp24/forms/timetable/HolidaySearch.php [new file with mode: 0755]
erp24/forms/timetable/IndexForm.php [new file with mode: 0755]
erp24/forms/timetable/StartForm.php [new file with mode: 0755]
erp24/forms/timetable/TabelSearchForm.php [new file with mode: 0755]
erp24/forms/timetable/TabelSearchFormFact.php [new file with mode: 0755]
erp24/forms/writeOffsErp/WriteOffsForm.php [new file with mode: 0644]
erp24/forms/writeOffsErp/WriteOffsProductsForm.php [new file with mode: 0644]
erp24/models/LoginForm.php
erp24/tests/functional/PageListCest.php [new file with mode: 0644]
erp24/tests/unit/models/UserTest.php
erp24/views/layouts/api2_menu.php
erp24/web/favicon.ico

index d9d401bc00d864eab81009d3d8f1981dbd753feb..050409b8bc260b12466bb86e7e626886271ea745 100644 (file)
@@ -6,7 +6,7 @@ RUN apk add --update --no-cache \
 
 RUN apk add --no-cache zlib libpng icu \
   && apk add --no-cache --virtual .deps zlib-dev libpng-dev icu-dev \
-  && docker-php-ext-install -j$(nproc) gd mysqli pdo pdo_mysql intl calendar opcache \
+  && docker-php-ext-install -j$(nproc) gd mysqli pdo pdo_mysql intl calendar mbstring opcache \
   && apk del .deps
 
 RUN apk --no-cache --update --repository http://dl-cdn.alpinelinux.org/alpine/v$ALPINE_VERSION/main/ add \
@@ -21,7 +21,7 @@ RUN wget https://getcomposer.org/installer \
 
 #add xdebug
 RUN apk add --no-cache $PHPIZE_DEPS \
-  && pecl install xdebug \
+  && pecl install xdebug-3.1.6 \
   && docker-php-ext-enable xdebug \
   && apk del $PHPIZE_DEPS
 WORKDIR /www
diff --git a/erp24/forms/EmployeePaymentSearch.php b/erp24/forms/EmployeePaymentSearch.php
new file mode 100755 (executable)
index 0000000..36e96c1
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+
+namespace yii_app\forms;
+
+use yii\base\Model;
+use yii\data\ActiveDataProvider;
+use yii_app\records\EmployeePayment;
+
+/**
+ * EmployeePaymentSearch represents the model behind the search form of `yii_app\records\EmployeePayment`.
+ */
+class EmployeePaymentSearch extends EmployeePayment
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function rules()
+    {
+        return [
+            [['id', 'admin_id', 'admin_group_id', 'creator_id'], 'integer'],
+            [['date'], 'safe'],
+            [['monthly_salary', 'daily_payment'], 'number'],
+        ];
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function scenarios()
+    {
+        // bypass scenarios() implementation in the parent class
+        return Model::scenarios();
+    }
+
+    /**
+     * Creates data provider instance with search query applied
+     *
+     * @param array $params
+     *
+     * @return ActiveDataProvider
+     */
+    public function search($params)
+    {
+        $query = EmployeePayment::find();
+
+        // add conditions that should always apply here
+
+        $dataProvider = new ActiveDataProvider([
+            'query' => $query,
+        ]);
+
+        $this->load($params);
+
+        if (!$this->validate()) {
+            // uncomment the following line if you do not want to return any records when validation fails
+            // $query->where('0=1');
+            return $dataProvider;
+        }
+
+        // grid filtering conditions
+        $query->andFilterWhere([
+            'id' => $this->id,
+            'admin_id' => $this->admin_id,
+            'admin_group_id' => $this->admin_group_id,
+            'date' => $this->date,
+            'monthly_salary' => $this->monthly_salary,
+            'daily_payment' => $this->daily_payment,
+            'creator_id' => $this->creator_id,
+        ]);
+
+        return $dataProvider;
+    }
+}
diff --git a/erp24/forms/MultipleUploadForm.php b/erp24/forms/MultipleUploadForm.php
new file mode 100644 (file)
index 0000000..0407560
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+
+namespace yii_app\forms;
+
+use yii\base\Model;
+use yii\web\UploadedFile;
+class MultipleUploadForm extends Model
+{
+    /**
+     * @var UploadedFile[] files uploaded
+     */
+    public $imageFiles;
+    /**
+     * @return array the validation rules.
+     */
+    public function rules()
+    {
+        return [
+            [['imageFiles'], 'file', 'extensions' => 'png, jpg', 'maxFiles' => 10, 'skipOnEmpty' => false],
+        ];
+    }
+
+    public function upload()
+    {
+        if ($this->validate()) {
+            foreach ($this->imageFiles as $file) {
+
+               $test = 33;
+//               $file->saveAs('uploads/' . $file->baseName . '.' . $file->extension);
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/erp24/forms/UploadImageForm.php b/erp24/forms/UploadImageForm.php
new file mode 100644 (file)
index 0000000..c80ae72
--- /dev/null
@@ -0,0 +1,38 @@
+<?php
+
+namespace yii_app\forms;
+
+use Yii;
+use yii\base\Model;
+use yii_app\records\Files;
+
+class UploadImageForm extends Model {
+    public $image;
+    public function rules() {
+        return [
+            [['image'], 'file', 'skipOnEmpty' => false, 'extensions' => 'jpg, png'],
+        ];
+    }
+    public function upload($entity, $entity_id) {
+        if ($this->validate()) {
+            $target_dir = 'uploads/' . Yii::$app->user->id . '/' . date("Y") . "/" . date("m") . "/" . date("d");
+            if (!is_dir($target_dir)) {
+                mkdir($target_dir, 0777, true);
+            }
+            $targetFile = $target_dir . "/" . rand(1000, 9999) . $this->image->baseName . '.' . $this->image->extension;
+            $this->image->saveAs($targetFile);
+
+            $fileRecord = new Files;
+            $fileRecord->created_at = date("Y-m-d H:i:s");
+            $fileRecord->entity_id = $entity_id;
+            $fileRecord->entity = $entity;
+            $fileRecord->file_type = 'image';
+            $fileRecord->url = '/' . $targetFile;
+            $fileRecord->save();
+
+            return true;
+        } else {
+            return false;
+        }
+    }
+}
\ No newline at end of file
diff --git a/erp24/forms/device/AddDeviceForm.php b/erp24/forms/device/AddDeviceForm.php
new file mode 100755 (executable)
index 0000000..03035f3
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+declare(strict_types = 1);
+
+namespace yii_app\forms\device;
+
+use yii\base\Model;
+use yii_app\records\AdminDesktop;
+use yii_app\records\CityStore;
+
+/**
+ * Форма добавления слотов
+ * @package yii_app\forms\timetable
+ */
+class AddDeviceForm extends Model
+{
+    public $storeId = null;
+    public $typeId = null;
+    public $name = null;
+
+    protected $device;
+
+    public function init()
+    {
+        $this->device = new AdminDesktop();
+    }
+    public function rules(): array
+    {
+        return [
+            [['storeId'], 'integer'],
+            [['name'], 'string'],
+            [['name'], 'default', 'value' => 'Рабочий ПК'],
+            [['storeId'], 'in', 'range' => array_keys(self::stores())],
+            [['typeId'], 'in', 'range' => array_keys(AdminDesktop::deviceTypes())],
+        ];
+    }
+
+    public function attributeLabels()
+    {
+        return [
+            'storeId' => 'Магазин',
+            'typeId' => 'Тип устройства',
+            'name' => 'Название устройства',
+        ];
+    }
+    public static function stores() : array
+    {
+        return CityStore::find()
+            ->select(['name', 'id'])
+            ->indexBy('id')
+            ->cache(3600)
+            ->column();
+    }
+    public static function devices()
+    {
+        return AdminDesktop::deviceTypes();
+    }
+
+    public function getStoreName()
+    {
+        return self::stores()[$this->storeId] ?? 'вне магазинов';
+    }
+
+    public function save(): bool
+    {
+        if (!$this->validate()) {
+            return false;
+        }
+        $this->device->name = $this->name;
+        $this->device->store_id = $this->storeId;
+        $this->device->date_add = date('Y-m-d H:i:s');
+        $this->device->type_id = $this->typeId;
+        $this->device->device_info = \Yii::$app->getRequest()->getUserAgent();
+        $this->device->lasttime = date('Y-m-d H:i:s');
+        $this->device->keygen = \Yii::$app->getSecurity()->generateRandomString();
+        $this->device->ip = \Yii::$app->getRequest()->getRemoteIP();
+        $this->device->admin_id = $_SESSION['admin_id'];
+        if (!$this->device->save()) {
+            $this->addErrors($this->device->getErrors());
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * @return AdminDesktop
+     */
+    public function getDevice()
+    {
+        return $this->device;
+    }
+}
\ No newline at end of file
diff --git a/erp24/forms/device/SelectDeviceForm.php b/erp24/forms/device/SelectDeviceForm.php
new file mode 100755 (executable)
index 0000000..67f8841
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+declare(strict_types = 1);
+
+namespace yii_app\forms\device;
+
+use yii\base\Model;
+use yii_app\records\AdminDesktop;
+use yii_app\records\CityStore;
+
+/**
+ * Форма добавления слотов
+ * @package yii_app\forms\timetable
+ */
+class SelectDeviceForm extends Model
+{
+    public $id = null;
+
+    public function rules(): array
+    {
+        return [
+            [['id'], 'integer'],
+            [['id'], 'in', 'range' => array_keys(self::devices())],
+        ];
+    }
+
+    public static function devices()
+    {
+        static $all = [];
+        if ($all) {
+            return $all;
+        }
+        return $all = AdminDesktop::find()->indexBy('id')->all();
+    }
+
+    public function attributeLabels()
+    {
+        return [
+            'id' => 'Устройство',
+        ];
+    }
+
+    /**
+     * @return array|\yii\db\ActiveRecord|null
+     */
+    public function getDevice()
+    {
+        return AdminDesktop::find()->andWhere(['id' => $this->id])->one();
+    }
+}
\ No newline at end of file
diff --git a/erp24/forms/infoTable/ChartManagementForm.php b/erp24/forms/infoTable/ChartManagementForm.php
new file mode 100644 (file)
index 0000000..76feb8e
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+
+namespace yii_app\forms\infoTable;
+
+use yii\base\Model;
+
+class ChartManagementForm extends Model
+{
+    public $date_start;
+
+    public $date_end;
+
+    public $mode_group;
+
+    public $mode_select_matrix_category;
+
+    public function rules()
+    {
+        return [
+            [[ 'date_start', 'date_end'], 'safe'],
+            [['mode_select_matrix_category', 'mode_group'], 'integer'],
+            ['date_start', 'default', 'value' => date("Y-m-d", strtotime('today -14 day'))],
+            ['date_end', 'default', 'value' => date("Y-m-d", strtotime('now'))],
+            ['mode_select_matrix_category', 'default', 'value' => 0],
+            ['mode_group', 'default', 'value' => 0],
+            ['date_start', 'validateDate'],
+        ];
+    }
+
+    public function attributeLabels()
+    {
+        return [
+            'date_start' => 'Дата начала',
+            'date_end' => 'Дата окончания',
+            'mode_select_matrix_category' => 'Определить категории матрицы'
+        ];
+    }
+
+    public function validateDate()
+    {
+        if (strtotime($this->date_start) > strtotime($this->date_end)) {
+            $this->addError('date_start', 'Дата начала не может быть больше даты окончания');
+            $this->addError('date_end', 'Дата окончания не может быть меньше даты начала');
+        }
+    }
+}
\ No newline at end of file
diff --git a/erp24/forms/infoTable/Charts.php b/erp24/forms/infoTable/Charts.php
new file mode 100644 (file)
index 0000000..fc80bd0
--- /dev/null
@@ -0,0 +1,39 @@
+<?php
+
+namespace yii_app\forms\infoTable;
+
+class Charts extends \yii\base\Model
+{
+
+    public $store_id;
+
+    public $date_start;
+
+    public $date_end;
+
+    public function rules()
+    {
+        return [
+            [['store_id', 'date_start', 'date_end'], 'safe'],
+            ['date_start', 'default', 'value' => date("Y-m-d", strtotime('first day of this month'))],
+            ['date_end', 'default', 'value' => date("Y-m-d", strtotime('last day of this month'))],
+            ['date_start', 'validateDate'],
+        ];
+    }
+
+    public function attributeLabels()
+    {
+        return [
+            'date_start' => 'Дата начала',
+            'date_end' => 'Дата окончания'
+        ];
+    }
+
+    public function validateDate()
+    {
+        if (strtotime($this->date_start) > strtotime($this->date_end)) {
+            $this->addError('date_start', 'Дата начала не может быть больше даты окончания');
+            $this->addError('date_end', 'Дата окончания не может быть меньше даты начала');
+        }
+    }
+}
\ No newline at end of file
diff --git a/erp24/forms/infoTable/YearWeekSearchForm.php b/erp24/forms/infoTable/YearWeekSearchForm.php
new file mode 100644 (file)
index 0000000..34e5431
--- /dev/null
@@ -0,0 +1,40 @@
+<?php
+declare(strict_types = 1);
+
+namespace yii_app\forms\infoTable;
+
+use yii\base\Model;
+
+class YearWeekSearchForm extends Model
+{
+    public string $year;
+    public string $week;
+    public string $dateFrom;
+    public string $dateTo;
+    public string $store_id;
+
+    public function rules(): array
+    {
+        return [
+            [['year', 'week', 'dateFrom', 'dateTo', 'store_id'], 'string'],
+            [['year', 'week'], 'required'],
+        ];
+    }
+    public function __construct()
+    {
+        $this->year = date("Y", strtotime(date("Y-m-d",time())));
+
+        $this->week = date("W", strtotime(date("Y-m-d",time())));
+    }
+
+
+    public function attributeLabels()
+    {
+        return [
+            'year' => 'Год',
+            'week' => 'Неделя',
+            'store_id' => 'Магазин',
+        ];
+    }
+
+}
\ No newline at end of file
diff --git a/erp24/forms/lesson/LessonForm.php b/erp24/forms/lesson/LessonForm.php
new file mode 100755 (executable)
index 0000000..6ba21be
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+declare(strict_types = 1);
+
+namespace yii_app\forms\lesson;
+
+use yii\base\Model;
+
+class LessonForm extends Model
+{
+    public $lesson_id = null;
+    public $name = null;
+    public $description = null;
+    public $status = 0;
+    public $content = null;
+    public $video_url = null;
+
+    public $obligatory_time = 0;
+    public $shuffle_polls = 0;
+    public $min_percent = 100;
+    public $max_attempts = 10;
+    public $recommended_time = 0;
+    public $attempt_delay = 0;
+    public $max_time = 60;
+    public $success_ball = 10;
+    public $fail_ball = 10;
+
+    public function rules(): array
+    {
+        return [
+            [['name', 'video_url'], 'required'],
+            [['lesson_id', 'min_percent', 'max_attempts', 'recommended_time', 'status', 'attempt_delay', 'max_time',
+                'success_ball', 'fail_ball', 'shuffle_polls', 'obligatory_time'], 'integer'],
+            [['name','description','content','video_url'], 'string'],
+        ];
+    }
+}
\ No newline at end of file
diff --git a/erp24/forms/log/ErrorLogSearchModel.php b/erp24/forms/log/ErrorLogSearchModel.php
new file mode 100755 (executable)
index 0000000..5ac5adf
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+declare(strict_types = 1);
+
+namespace yii_app\forms\log;
+
+use yii\base\Model;
+
+/**
+ * Фильтр ошибок
+ * @package yii_app\forms\timetable
+ */
+class ErrorLogSearchModel extends Model
+{
+    public $id;
+    public $ip;
+    public $line;
+    public $col;
+    public $level;
+    public $category;
+
+    public function rules()
+    {
+        return [
+            [['id'], 'integer'],
+            [['ip', 'category'], 'string'],
+            [['line', 'col','level'], 'string'],
+        ];
+    }
+}
\ No newline at end of file
diff --git a/erp24/forms/payroll/YearMonthSearchForm.php b/erp24/forms/payroll/YearMonthSearchForm.php
new file mode 100755 (executable)
index 0000000..3f40b52
--- /dev/null
@@ -0,0 +1,46 @@
+<?php
+declare(strict_types = 1);
+
+namespace yii_app\forms\payroll;
+
+use yii\base\Model;
+
+class YearMonthSearchForm extends Model
+{
+    public string $year;
+    public string $month;
+    public string $dateFrom;
+    public string $dateTo;
+    public string $store_id;
+
+    public function rules(): array
+    {
+        return [
+            [['year', 'month', 'dateFrom', 'dateTo', 'store_id'], 'string'],
+            [['year', 'month'], 'required'],
+        ];
+    }
+    public function __construct()
+    {
+        $dayPastCurrentMonth = (int) date("d");
+        $modifier = "";
+        if ($dayPastCurrentMonth < 8) {
+            $modifier = " -1 months";
+        }
+        $this->year = date("Y", strtotime(date("Y-m-d",time()) . $modifier));
+
+        $this->month = date("m", strtotime(date("Y-m-d",time()) . $modifier));
+    }
+
+
+    public function attributeLabels()
+    {
+        return [
+            'year' => 'Год',
+            'month' => 'Месяц',
+            'dateFrom' => 'dateFrom',
+            'store_id' => 'Магазин',
+        ];
+    }
+
+}
\ No newline at end of file
diff --git a/erp24/forms/planStore/AddPlanStore.php b/erp24/forms/planStore/AddPlanStore.php
new file mode 100644 (file)
index 0000000..d6c8e35
--- /dev/null
@@ -0,0 +1,52 @@
+<?php
+
+namespace yii_app\forms\planStore;
+
+use yii\base\Model;
+
+class AddPlanStore extends Model
+{
+    public $store_id;
+
+    public $date_str;
+
+    public $year;
+
+    public $month;
+
+    public static $month_name = [
+        1 => 'Январь',
+        'Февраль',
+        'Март',
+        'Апрель',
+        'Май',
+        'Июнь',
+        'Июль',
+        'Август',
+        'Сентябрь',
+        'Октябрь',
+        'Ноябрь',
+        'Декабрь'
+    ];
+    public function rules()
+    {
+        return [
+            [['store_id', 'year', 'month'], 'required'],
+            [['store_id', 'year', 'month'], 'integer'],
+            ['date_str', 'string']
+        ];
+    }
+
+    public function attributeLabels()
+    {
+        return [
+            'store_id' => 'Магазин',
+            'date_str' => 'Дата'
+        ];
+    }
+
+    public function updateDate () {
+        $this->year = intval(date('Y', strtotime($this->date_str)));
+        $this->month = intval(date('m', strtotime($this->date_str)));
+    }
+}
\ No newline at end of file
diff --git a/erp24/forms/support/TaskSupportCreateForm.php b/erp24/forms/support/TaskSupportCreateForm.php
new file mode 100755 (executable)
index 0000000..9855cd1
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+
+namespace yii_app\forms\support;
+
+use yii_app\records\Task;
+
+class TaskSupportCreateForm extends Task
+{
+    public function rules()
+    {
+        return [
+            [['name', 'description', 'entity_type', 'task_type_id', 'duration', 'alert_level_id', 'motivation_id'], 'required'],
+            [['description',], 'string'],
+            [['name'], 'string', 'max' => 255],
+            [['name'], 'string', 'min' => 3],
+            [['entity_id', 'function_entity_id'], 'string', 'max' => 36],
+            [['files'], 'file', 'skipOnEmpty' => true, 'maxFiles' => 20],
+        ];
+    }
+
+    public function attributeLabels()
+    {
+        return [
+            'description' => 'Описание',
+            'entity_type' => 'Тип сущности',
+            'entity_id' => 'Магазин',
+            'name' => 'Название'
+        ];
+    }
+}
\ No newline at end of file
diff --git a/erp24/forms/timetable/AddForm.php b/erp24/forms/timetable/AddForm.php
new file mode 100755 (executable)
index 0000000..8ce1dcf
--- /dev/null
@@ -0,0 +1,146 @@
+<?php
+declare(strict_types = 1);
+
+namespace yii_app\forms\timetable;
+
+use yii\base\Model;
+use yii\db\Expression;
+use yii_app\records\Admin;
+use yii_app\records\AdminGroup;
+use yii_app\records\CityStore;
+use yii_app\records\Shift;
+use yii_app\records\Timetable;
+use yii_app\records\TimetablePlan;
+
+/**
+ * Форма добавления слотов
+ * @property-read Shift $shift
+ * @package yii_app\forms\timetable
+ */
+class AddForm extends Model
+{
+    public $storeId = null;
+    public $date = null;
+    public $timeStart = null;
+    public $timeEnd = null;
+    public $shiftId = null;
+    public $adminIds = null;
+    public $slotTypeId = null;
+    public $comment = '';
+
+    public function rules(): array
+    {
+        return [
+            [['storeId', 'shiftId'], 'integer'],
+            [['storeId'], 'in', 'range' => array_keys(self::stores())],
+            [['date'], 'date', 'format' => 'php:Y-m-d'],
+            [['timeStart', 'timeEnd', 'adminIds'], 'required'],
+            [['timeStart', 'timeEnd'], 'date', 'format' => 'php:H:i:s'],
+            [['shiftId'], 'in', 'range' => array_keys(Shift::all())],
+            [['adminIds'], 'each', 'rule' => ['in', 'range' => array_keys(self::admins())]],
+            [['slotTypeId'], 'in', 'range' => array_keys(Timetable::slotTypeName())],
+        ];
+    }
+
+    public function attributeLabels()
+    {
+        return [
+            'adminIds' => 'Пользователи',
+            'storeId' => 'Магазин',
+            'timeStart' => 'Время начала',
+            'timeEnd' => 'Время конца',
+            'shiftId' => 'Смена',
+            'slotTypeId' => 'Тип занятости',
+        ];
+    }
+    public static function stores() : array
+    {
+        return CityStore::find()
+            ->select(['name', 'id'])
+            ->andWhere(['visible' => 1])
+            ->indexBy('id')
+            ->cache(3600)
+            ->column();
+    }
+
+    public function getShift(): Shift
+    {
+        return Shift::all()[$this->shiftId];
+    }
+
+    public static function admins()
+    {
+        return Admin::find()
+            ->with('adminGroup')
+            ->andWhere([
+                'group_id' => array_keys(AdminGroup::groupsWithShift()),
+            ])
+            ->orderBy([
+                'group_id' => SORT_ASC,
+                'name' => SORT_ASC
+            ])
+            ->indexBy('id')
+            ->all()
+            ;
+    }
+
+    public static function minutes(): array
+    {
+        return [0, 10, 20, 30, 40, 50];
+    }
+
+    public function getStoreName()
+    {
+        return self::stores()[$this->storeId] ?? 'вне магазинов';
+    }
+
+    public function save(): bool
+    {
+        if (!$this->validate()) {
+            return false;
+        }
+        $start = new \DateTime($this->date . ' ' .$this->timeStart);
+        $end = new \DateTime($this->date . ' ' .$this->timeEnd);
+        $minStart = new \DateTime($this->date . ' ' .$this->shift->start_time);
+        // shift crosses midnight
+        if ($start < $minStart) {
+            $start->modify('+1 day');
+        }
+        if ($end < $minStart) {
+            $end->modify('+1 day');
+        }
+
+        /** @var Admin[] $admins */
+        $admins = Admin::find()
+            ->with('adminGroup')
+            ->andWhere(['id' => $this->adminIds])
+            ->indexBy('id')
+            ->all();
+
+        $trx = TimetablePlan::getDb()->beginTransaction();
+        foreach ($this->adminIds as $adminId) {
+            $timeTable = new TimetablePlan([
+                'shift_id' => $this->shiftId,
+                'store_id' => $this->storeId,
+                'date' => $this->date,
+                'admin_id' => $adminId,
+                'd_id' => $admins[$adminId]->adminGroup->id,
+                'admin_id_add' => $_SESSION['admin_id'],
+                'time_start' => $this->timeStart,
+                'time_end' => $this->timeEnd,
+                'datetime_start' => $start->format('Y-m-d H:i:s'),
+                'datetime_end' => $end->format('Y-m-d H:i:s'),
+                'slot_type_id' => $this->slotTypeId,
+                'comment' => $this->comment,
+                'status' => 0,
+            ]);
+            if (!$timeTable->save()) {
+                $this->addError('adminIds', implode("\n", $timeTable->getFirstErrors()));
+                $trx->rollBack();
+                return false;
+            }
+        }
+        $trx->commit();
+        return true;
+    }
+}
\ No newline at end of file
diff --git a/erp24/forms/timetable/HolidaySearch.php b/erp24/forms/timetable/HolidaySearch.php
new file mode 100755 (executable)
index 0000000..6b225e1
--- /dev/null
@@ -0,0 +1,35 @@
+<?php
+declare(strict_types = 1);
+
+namespace yii_app\forms\timetable;
+
+use yii\base\Model;
+
+class HolidaySearch extends Model
+{
+    public $dateFrom = null;
+    public $dateTo = null;
+
+    public function rules(): array
+    {
+        return [
+            [['dateFrom', 'dateTo'], 'string'],
+            [['dateFrom', 'dateTo'], 'date', 'format' => 'php:Y-m-d'],
+            [['dateFrom'], 'default', 'value' => function () {
+                return date('Y-01-01');
+            }],
+            [['dateTo'], 'default', 'value' => function () {
+                return date('Y-12-31');
+            }],
+        ];
+    }
+
+    public function attributeLabels()
+    {
+        return [
+            'dateFrom' => 'Дата начала',
+            'dateTo' => 'Дата конца',
+        ];
+    }
+
+}
\ No newline at end of file
diff --git a/erp24/forms/timetable/IndexForm.php b/erp24/forms/timetable/IndexForm.php
new file mode 100755 (executable)
index 0000000..d5dfbaa
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+declare(strict_types = 1);
+
+namespace yii_app\forms\timetable;
+
+use yii\base\Model;
+use yii_app\records\CityStore;
+
+class IndexForm extends Model
+{
+    public $store_id = null;
+    public $start = null;
+    public $end = null;
+
+    public function rules(): array
+    {
+        return [
+            [['store_id'], 'integer'],
+            [['store_id'], 'in', 'range' => array_keys(self::stores())],
+            [['start', 'end'], 'date', 'format' => 'php:Y-m-d'],
+            [['start'], 'default', 'value' => date('Y-m-d', strtotime('-1 day'))],
+            [['end'], 'default', 'value' => date('Y-m-d', strtotime('+7 day'))],
+        ];
+    }
+
+    public function attributeLabels()
+    {
+        return [
+            'store_id' => 'Магазин',
+            'start' => 'Дата начала',
+            'end' => 'Дата конца',
+        ];
+    }
+    public static function stores() : array
+    {
+        return CityStore::find()
+            ->select(['name', 'id'])
+            ->andWhere(['visible' => 1])
+            ->indexBy('id')
+            ->cache(3600)
+            ->column();
+    }
+
+    public function getStoreName()
+    {
+        return self::stores()[$this->store_id] ?? 'вне магазинов';
+    }
+
+}
\ No newline at end of file
diff --git a/erp24/forms/timetable/StartForm.php b/erp24/forms/timetable/StartForm.php
new file mode 100755 (executable)
index 0000000..3345973
--- /dev/null
@@ -0,0 +1,251 @@
+<?php
+declare(strict_types = 1);
+
+namespace yii_app\forms\timetable;
+
+use yii\base\Model;
+use yii\helpers\ArrayHelper;
+use yii\web\UploadedFile;
+use yii_app\records\Admin;
+use yii_app\records\AdminCheckin;
+use yii_app\records\AdminDesktop;
+use yii_app\records\AdminGroup;
+use yii_app\records\CityStore;
+use yii_app\records\Shift;
+use yii_app\records\Timetable;
+use yii_app\records\TimetablePlan;
+
+/**
+ * Форма добавления слотов
+ * @property CityStore $store
+ * @property TimetablePlan $planSlot
+ * @package yii_app\forms\timetable
+ */
+class StartForm extends Model
+{
+    public $id;
+    public $admin_id;
+    public $type_id;
+    public $date;
+    public $time;
+    public $store_id;
+    public $ball;
+    public $comment = '';
+    /** @var UploadedFile */
+    public $photo;
+    public $d_id;
+    public $status = 0; // не проверено
+    public $lat;
+    public $lon;
+    /** @var int */
+    public $replaced_admin_id;
+    /** @var int */
+    public $plan_id;
+    /** @var int */
+    public $device_id;
+    public $checkin_id;
+
+    /** @var AdminCheckin */
+    public $checkinModel;
+    /** @var TimetablePlan */
+    private $planSlotModel;
+
+    public function isStart()
+    {
+        return $this->type_id == AdminCheckin::TYPE_START;
+    }
+
+    public function isEnd()
+    {
+        return $this->type_id == AdminCheckin::TYPE_END;
+    }
+
+    public function isAppear()
+    {
+        return $this->type_id == AdminCheckin::TYPE_APPEAR;
+    }
+
+    public static function ratings()
+    {
+        return [
+            2 => 'Неудовлетворительно',
+            3 => 'Удовлетворительно',
+            4 => 'Хорошо',
+            5 => 'Отлично',
+        ];
+    }
+
+    public function attributeLabels()
+    {
+        return [
+            'id' => 'ID',
+            'admin_id' => 'ID пользователя',
+            'type_id' => 'Открытие/закрытие смены',
+            'date' => 'Дата смены',
+            'time' => 'Время',
+            'store_id' => 'Место',
+            'checkin_id' => 'ID отметки / открытия / закрытия смены',
+            'ball' => 'Оценка',
+            'comment' => 'Комментарий',
+            'photo' => 'Селфи',
+            'd_id' => 'Должность',
+            'status' => 'Статус проверки',
+            'replaced_admin_id' => 'Заменяемый сотрудник',
+            'gps' => 'Координаты',
+        ];
+    }
+
+    public function init()
+    {
+        $this->checkinModel = new AdminCheckin();
+    }
+
+    public function rules(): array
+    {
+        return [
+            [['id', 'admin_id', 'replaced_admin_id', 'plan_id', 'checkin_id', 'store_id', 'type_id', 'd_id', 'ball', 'status'], 'integer'],
+            [['comment', 'date'], 'string'],
+            [['lat', 'lon'], 'number'],
+            [['admin_id', 'replaced_admin_id'], 'exist', 'targetClass' => Admin::class, 'targetAttribute' => 'id'],
+            [['admin_id', 'store_id', 'device_id', 'type_id', 'd_id', 'ball', 'date'], 'required'],
+            [['d_id'], 'exist', 'targetClass' => AdminGroup::class, 'targetAttribute' => 'id'],
+            [['store_id'], function() {
+                $targetAdminId = $this->replaced_admin_id ?: $this->admin_id;
+                /** @var Admin $targetAdmin */
+                $targetAdmin = Admin::find()->andWhere(['id' => $targetAdminId])->one();
+                if (!in_array($this->store_id, $targetAdmin->getStoreIds())) {
+                    $this->addError('store_id', 'У пользователя нет доступа к этому магазину');
+                }
+            }],
+            [['status'], 'required', 'requiredValue' => AdminCheckin::STATUS_PENDING],
+            [['photo'], 'required', 'message' => 'Смена не может быть открыта без селфи'],
+            [['photo'], 'file', 'mimeTypes' => ['image/jpeg'], 'message' => 'Неправильный формат файла'],
+            [['store_id'], 'required', 'message' => 'Смена не может быть открыта без указания магазина'],
+            [['time'], function () {
+                $maxShiftDuration = max(ArrayHelper::getColumn(Shift::all(), 'duration'));
+                /** @var AdminCheckin $lastCheckin */
+                $lastCheckin = AdminCheckin::find()
+                    ->andWhere(['admin_id' => $this->admin_id])
+                    ->andWhere(['>', 'time', date('Y-m-d H:i:s', strtotime("-$maxShiftDuration hour"))])
+                    ->orderBy(['time' => SORT_DESC])
+                    ->limit(1)
+                    ->one();
+                if (!$lastCheckin) {
+                    return;
+                }
+                if ($lastCheckin->type_id == $this->type_id) {
+                    if ($this->isStart()) {
+                        $this->addError('time', 'Смена уже открывалась');
+                    } else {
+                        $this->addError('time', 'Смена уже закрывалась');
+                    }
+                }
+            }],
+            [['admin_id'], function () {
+                /** @TODO check for power admins */
+                if ($this->admin_id != $_SESSION['admin_id']) {
+                    $this->addError('admin_id', 'Неправильный пользователь');
+                }
+            }],
+            [['device_id'], function () {
+                /** @var AdminDesktop $device */
+                $device = AdminDesktop::find()->andWhere(['id' => $this->device_id])->one();
+                if (!$device) {
+                    $this->addError('device_id', 'Неправильный идентификатор устройства');
+                }
+                if ($device->keygen != $_COOKIE['device_key']) {
+                    $this->addError('device_id', 'Переданный идентификатор не совпадает с привязанным устройством');
+                }
+            }],
+            [['plan_id'], function () {
+                if (!$this->plan_id) {
+                    return;
+                }
+                /** @var TimetablePlan $plan */
+                $plan = TimetablePlan::find()->andWhere(['id' => $this->plan_id])->one();
+                if (!$plan) {
+                    $this->addError('plan_id', 'Неправильный номер записи плана');
+                }
+                if (!$plan->isWorkSlot()) {
+                    $this->addError('plan_id', 'Нерабочий день по плану');
+                }
+                if ($this->date != $plan->date) {
+                    $this->addError('date', 'Дата открытия не совпадает с планом');
+                }
+                $targetAdminId = $this->replaced_admin_id ?: $this->admin_id;
+                if ($targetAdminId != $plan->admin_id) {
+                    $this->addError($this->replaced_admin_id ? 'replaced_admin_id' : 'admin_id', 'Выбран неправильный пользователь в плане');
+                }
+                if ($this->d_id != $plan->d_id) {
+                    $this->addError('d_id', 'Выбранная должность не совпадает с планом');
+                }
+                if ($this->date != $plan->date) {
+                    $this->addError('date', 'Дата не совпадает с планом');
+                }
+            }],
+        ];
+    }
+
+    public function beforeValidate()
+    {
+        $this->time = date('Y-m-d H:i:s');
+        return parent::beforeValidate();
+    }
+
+    public function save()
+    {
+        if (!$this->validate()) {
+            return false;
+        }
+        $filePath = $this->generateFilePath();
+        if ($this->hasErrors()) {
+            parent::afterValidate();
+            return false;
+        }
+
+        if (!$this->photo->saveAs($filePath)) {
+            $this->addError('photo', 'Невозможно загрузить файл фотографии');
+            parent::afterValidate();
+            return false;
+        }
+
+        $attributes = ['photo' => $filePath] + $this->getAttributes();
+        $this->checkinModel->setAttributes($attributes);
+        if (!$this->checkinModel->save()) {
+            $this->addErrors($this->checkinModel->getErrors());
+            return false;
+        }
+        return true;
+    }
+
+    private function generateFilePath(): string
+    {
+        $Y = date("Y");
+        $m = date("m");
+        if (!is_dir("data/admin/$Y/$m")) {
+            mkdir("data/admin/$Y/$m", 0777, true);
+        }
+        $fileName = $_SESSION["admin_id"] . '-' . date("YmdHis") . '.jpg';
+        return "data/admin/$Y/$m/$fileName";
+    }
+
+    public function getStore()
+    {
+        return CityStore::find()
+            ->andWhere(['id' => $this->store_id])
+            ->one()
+            ;
+    }
+
+    /**
+     * @return TimetablePlan
+     */
+    public function getPlanSlot()
+    {
+        if (!$this->planSlotModel) {
+            $this->planSlotModel = TimetablePlan::find()->andWhere(['id' => $this->plan_id])->one();
+        }
+        return $this->planSlotModel;
+    }
+
+}
\ No newline at end of file
diff --git a/erp24/forms/timetable/TabelSearchForm.php b/erp24/forms/timetable/TabelSearchForm.php
new file mode 100755 (executable)
index 0000000..cd30346
--- /dev/null
@@ -0,0 +1,147 @@
+<?php
+declare(strict_types = 1);
+
+namespace yii_app\forms\timetable;
+
+use yii\base\Model;
+use yii\db\ActiveQuery;
+use yii\db\Expression;
+use yii_app\records\Admin;
+use yii_app\records\AdminGroup;
+use yii_app\records\CityStore;
+use yii_app\records\Holiday;
+use yii_app\records\Timetable;
+use yii_app\records\TimetableFact;
+use yii_app\records\TimetablePlan;
+
+class TabelSearchForm extends Model
+{
+    public $storeId = null;
+    public $start = null;
+    public $end = null;
+    public $adminGroupId = '';
+    public $adminId = '';
+    public $status = '';
+
+    public function rules(): array
+    {
+        return [
+            [['storeId', 'adminGroupId', 'status'], 'integer'],
+            [['storeId'], 'in', 'range' => array_keys(self::stores())],
+            [['adminGroupId'], 'in', 'range' => array_keys(AdminGroup::groupsWithShift())],
+            [['start', 'end'], 'date', 'format' => 'php:Y-m-d'],
+            [['status'], 'in' ,'range' => array_keys(Timetable::statuses())],
+            [['start'], 'default', 'value' => date('Y-m-01', strtotime('next month midnight'))],
+            [['end'], 'default', 'value' => date('Y-m-t', strtotime('next month midnight'))],
+        ];
+    }
+
+    public function attributeLabels()
+    {
+        return [
+            'storeId' => 'Магазин',
+            'start' => 'Дата начала',
+            'end' => 'Дата конца',
+            'adminGroupId' => 'Группа работников',
+        ];
+    }
+
+    public static function stores() : array
+    {
+        $stores = CityStore::find()
+            ->select(['name', 'id'])
+            ->andWhere(['id' => $_SESSION["store_arr_dostup"]])
+            ->indexBy('id')
+            ->cache(3600)
+            ->column();
+        natsort($stores);
+        return $stores;
+    }
+
+    public static function adminGroups(): array
+    {
+        static $groups;
+        if (isset($groups)) {
+            return $groups;
+        }
+        return \Yii::$app->getCache()->getOrSet('admin_groups_with_shifts', function () {
+            return AdminGroup::find()
+                ->with(['shift'])
+                ->andWhere(['NOT IN', 'id', [1, 2, -1]])
+                ->indexBy('id')
+                ->all();
+        });
+    }
+
+    /**
+     * @return Admin[]
+     */
+    public function admins()
+    {
+        $adminQuery = Admin::find()
+            ->select(['id', 'name', 'group_id', 'store_arr', 'avatarka'])
+            ->with(['adminGroup' => function (ActiveQuery $q) {
+                return $q->select(['id', 'name']);
+            }])
+            ->with('adminGroup.shift')
+            ->andWhere([
+                'group_id' => array_keys(AdminGroup::groupsWithShift())
+            ])
+            ->andFilterWhere(['group_id' => $this->adminGroupId])
+            ->orderBy(['group_id' => SORT_ASC, 'name' => SORT_ASC])
+            ->indexBy('id');
+        if ($this->storeId) {
+            $adminQuery
+            ->andWhere(new Expression('FIND_IN_SET(:store_id, store_arr)', ['store_id' => (int) $this->storeId]));
+        }
+        return $adminQuery->all();
+    }
+
+    public function search($tabel)
+    {
+        $classes = [
+            Timetable::TABLE_PLAN => TimetablePlan::class,
+            Timetable::TABLE_FACT => TimetableFact::class,
+        ];
+        if (!isset($classes[$tabel])) {
+            throw new \Exception('Unknown type');
+        }
+        /** @var TimetablePlan | TimetableFact $class */
+        $class = $classes[$tabel];
+        return $class::find()
+            ->andWhere([
+                'admin_group_id' => array_keys(AdminGroup::groupsWithShift()),
+                'store_id' => array_keys(self::stores()),
+            ])
+            ->andFilterWhere([
+                'admin_group_id' => $this->adminGroupId,
+                'store_id' => $this->storeId,
+                'status' => $this->status,
+            ])
+            ->andFilterWhere(['>=', 'date', $this->start])
+            ->andFilterWhere(['<=', 'date', $this->end])
+            ;
+    }
+
+    public function holidays()
+    {
+        return $this->validate(['start', 'end'], false) ? Holiday::find()
+            ->andFilterWhere(['>=', 'date', $this->start])
+            ->andFilterWhere(['<=', 'date', $this->end])
+            ->all() : []
+            ;
+    }
+
+    public function getDates(): array
+    {
+        $startDatetime = new \DateTime($this->start);
+        $endDatetime = new \DateTime($this->end);
+
+        $dates = [];
+        for ($date = clone $startDatetime; $date <= $endDatetime; $date->modify('+1 day')) {
+            $dates[] = clone $date;
+        }
+
+        return $dates;
+    }
+}
\ No newline at end of file
diff --git a/erp24/forms/timetable/TabelSearchFormFact.php b/erp24/forms/timetable/TabelSearchFormFact.php
new file mode 100755 (executable)
index 0000000..40be10f
--- /dev/null
@@ -0,0 +1,124 @@
+<?php
+declare(strict_types = 1);
+
+namespace yii_app\forms\timetable;
+
+use yii\base\Model;
+use yii\db\ActiveQuery;
+use yii\db\Expression;
+use yii_app\records\Admin;
+use yii_app\records\AdminGroup;
+use yii_app\records\CityStore;
+use yii_app\records\Timetable;
+use yii_app\records\TimetableFact;
+use yii_app\records\TimetablePlan;
+
+class TabelSearchFormFact extends Model
+{
+    public $storeId = null;
+    public $start = null;
+    public $end = null;
+    public $adminGroupId = '';
+    public $adminId = '';
+    public $status = '';
+
+    public function rules(): array
+    {
+        return [
+            [['storeId', 'adminGroupId', 'status'], 'integer'],
+            [['storeId'], 'in', 'range' => array_keys(self::stores())],
+            [['adminGroupId'], 'in', 'range' => array_keys(AdminGroup::groupsWithShift())],
+            [['start', 'end'], 'date', 'format' => 'php:Y-m-d'],
+            [['status'], 'in' ,'range' => array_keys(Timetable::statuses())],
+            [['status'], 'default' ,'value' => Timetable::STATUS_PENDING],
+            [['start'], 'default', 'value' => date('Y-m-d', strtotime('-7 day'))],
+            [['end'], 'default', 'value' => date('Y-m-d', strtotime('+1 day'))],
+        ];
+    }
+
+    public function attributeLabels()
+    {
+        return [
+            'storeId' => 'Магазин',
+            'start' => 'Дата начала',
+            'end' => 'Дата конца',
+            'adminGroupId' => 'Группа работников',
+            'status' => 'Статус проверки',
+        ];
+    }
+
+    public static function stores() : array
+    {
+        return CityStore::find()
+            ->select(['name', 'id'])
+            ->andWhere(['id' => $_SESSION["store_arr_dostup"]])
+            ->indexBy('id')
+            ->cache(3600)
+            ->column();
+    }
+
+    public static function adminGroups(): array
+    {
+        static $groups;
+        if (isset($groups)) {
+            return $groups;
+        }
+        return \Yii::$app->getCache()->getOrSet('admin_groups_with_shifts', function () {
+            return AdminGroup::find()
+                ->with(['shift'])
+                ->andWhere(['NOT IN', 'id', [1, 2, -1]])
+                ->indexBy('id')
+                ->all();
+        });
+    }
+
+    /**
+     * @return Admin[]
+     */
+    public function admins()
+    {
+        $adminQuery = Admin::find()
+            ->select(['id', 'name', 'group_id', 'store_arr'])
+            ->with(['adminGroup' => function (ActiveQuery $q) {
+                return $q->select(['id', 'name']);
+            }])
+            ->with('adminGroup.shift')
+            ->andWhere([
+                'group_id' => array_keys(AdminGroup::groupsWithShift())
+            ])
+            ->andFilterWhere(['group_id' => $this->adminGroupId])
+            ->orderBy(['group_id' => SORT_ASC, 'name' => SORT_ASC])
+            ->indexBy('id');
+        if ($this->storeId) {
+            $adminQuery
+            ->andWhere(new Expression('FIND_IN_SET(:store_id, store_arr)', ['store_id' => (int) $this->storeId]));
+        }
+        return $adminQuery->all();
+    }
+
+    public function search($tabel)
+    {
+        $classes = [
+            Timetable::TABLE_PLAN => TimetablePlan::class,
+            Timetable::TABLE_FACT => TimetableFact::class,
+        ];
+        if (!isset($classes[$tabel])) {
+            throw new \Exception('Unknown type');
+        }
+        /** @var TimetablePlan | TimetableFact $class */
+        $class = $classes[$tabel];
+        return $class::find()
+            ->andWhere([
+                'admin_group_id' => array_keys(AdminGroup::groupsWithShift()),
+                'store_id' => array_keys(self::stores()),
+            ])
+            ->andFilterWhere([
+                'admin_group_id' => $this->adminGroupId,
+                'store_id' => $this->storeId,
+                'status' => $this->status,
+            ])
+            ->andFilterWhere(['>=', 'date', $this->start])
+            ->andFilterWhere(['<=', 'date', $this->end])
+            ;
+    }
+}
\ No newline at end of file
diff --git a/erp24/forms/writeOffsErp/WriteOffsForm.php b/erp24/forms/writeOffsErp/WriteOffsForm.php
new file mode 100644 (file)
index 0000000..346e49f
--- /dev/null
@@ -0,0 +1,34 @@
+<?php
+declare(strict_types = 1);
+
+namespace yii_app\forms\writeOffsErp;
+
+use yii\base\Model;
+
+class WriteOffsForm extends Model
+{
+    public $store_id;
+    public $modelsProducts;
+
+    public function rules(): array
+    {
+        return [
+            [['store_id',], 'integer'],
+            [['modelsProducts',], 'safe'],
+            [['store_id'], 'required'],
+        ];
+    }
+    public function __construct()
+    {
+
+    }
+
+
+    public function attributeLabels()
+    {
+        return [
+            'store_id' => 'Магазин',
+        ];
+    }
+
+}
\ No newline at end of file
diff --git a/erp24/forms/writeOffsErp/WriteOffsProductsForm.php b/erp24/forms/writeOffsErp/WriteOffsProductsForm.php
new file mode 100644 (file)
index 0000000..43d0818
--- /dev/null
@@ -0,0 +1,111 @@
+<?php
+declare(strict_types = 1);
+
+namespace yii_app\forms\writeOffsErp;
+
+use yii\base\Model;
+use yii_app\records\Balances;
+use yii_app\records\CityStore;
+use yii_app\records\Products1c;
+
+class WriteOffsProductsForm extends Model
+{
+    public $product_id;
+    public $cause_id;
+    public $store_id;
+    public $quantity;
+    public $images;
+
+
+    private static function checkProductBalance($productGuid, $storeId, $quantity)
+    {
+
+        $product = Products1c::getProduct1c($productGuid, 'products');
+
+        if (empty($product)) {
+            $error_text = 'Выбранный продукт ' . $productGuid . ' не найден в справочнике соответствия ERP с продуктами 1с';
+        } else {
+            $store = CityStore::getCityStoreById( (int) $storeId, true);
+
+            $storeGuid = $store['storeGuid']['export_val'];
+
+            $productBalance = self::getProductBalance($productGuid, $storeGuid);
+
+            $productName = $product['name'];
+            $storeName = $store['name'];
+
+            $error_text = '';
+            $error_header_text = 'В магазине "' . $storeName . '" товара "' . $productName . '" на остатках';
+
+            if (0 == $productBalance) {
+                $error_text .= $error_header_text . ' нет';
+            } else if ($productBalance < $quantity) {
+                $delta = $quantity - $productBalance;
+                $error_text .= $error_header_text . ' недостаточно для списания.';
+                $error_text .= ' Необходимое количество для списания ' . $quantity . ' шт. , сейчас в магазине числится  ' . $productBalance . ' шт.';
+                $error_text .= ' Для списания нужно ещё ' . $delta . ' шт.';
+            }
+        }
+
+        return $error_text;
+
+    }
+
+    private static function getProductBalance($productGuid, $storeGuid)
+    {
+        $productCount = Balances::find()
+            ->select('quantity')
+            ->andWhere([
+            'product_id' => $productGuid,
+            'store_id' => $storeGuid,  //'quantity'
+        ])->asArray()
+
+            ->scalar();
+
+
+        return $productCount;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function rules()
+    {
+        return [
+            [['product_id', 'quantity'], 'required'],
+            [['store_id', 'cause_id'], 'integer'],
+            [['quantity'], 'number'],
+            [['product_id'], 'string', 'max' => 36],
+            ['quantity', 'compare', 'compareValue' => 0, 'operator' => '>'],
+            ['product_id', 'custom_function_validation'],
+        ];
+    }
+    public function custom_function_validation($attribute, $params)
+    {
+        if (empty($this->quantity) || empty($this->store_id)) {
+            $errorText = 'Проверка по наличию товара под списание в магазине не пройдена';
+            $this->addError($attribute, $errorText);
+        } else {
+            $resultCheck = self::checkProductBalance($this->$attribute, $this->store_id, $this->quantity);
+
+            if (!empty($resultCheck)) {
+                $this->addError($attribute, $resultCheck);
+            }
+        }
+
+    }
+
+
+/**
+     * {@inheritdoc}
+     */
+    public function attributeLabels()
+    {
+        return [
+            'product_id' => 'Товар',
+            'cause_id' => 'Причина списания',
+            'quantity' => 'Количество',
+        ];
+    }
+
+}
\ No newline at end of file
index e86786f675fdb7a852590ed87afd2a561ba72696..f68ba61ae94ef28cb96992e8e6a56058bb18331f 100644 (file)
@@ -103,6 +103,8 @@ class LoginForm extends Model
         $_SESSION['dostup_area'] = [];
         $group = AdminGroup::findOne($user->group_id);
         $_SESSION['admin_group_name'] = $group->name ?? "Какая-то...";
+        $_SESSION['name_group_admin'] = $group->name ?? "Какая-то...";
+
         $_SESSION['status_dostup_arr'] = [];
         $_SESSION['admin_group_add_arr'] = [];
 
@@ -123,7 +125,6 @@ class LoginForm extends Model
         $_SESSION['manager_id'] = $user->manager_id ?? null;
         $_SESSION['content_dostup'] = $user->content_dostup ?? null;
 
-        $_SESSION['name_group_admin'] = "Пользователь";
     }
 
     /**
diff --git a/erp24/tests/functional/PageListCest.php b/erp24/tests/functional/PageListCest.php
new file mode 100644 (file)
index 0000000..c9a1e60
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+
+namespace app\tests\functional;
+class PageListCest
+{
+    public function shiftSalesInterface(\FunctionalTester $I)
+    {
+        $I->amLoggedInAs(\app\models\User::findByUsername('admin'));
+        $I->amOnPage('/info-table/shift-sales');
+        $I->see('Login', 'h1');
+    }
+
+}
\ No newline at end of file
index 28986cbbd805905a2fa5f4fc3c63b32eb91afdeb..697e14590b3eb9e3fa63bd104fe78f7f98dbd41b 100644 (file)
@@ -3,13 +3,14 @@
 namespace tests\unit\models;
 
 use app\models\User;
+use yii_app\records\Admin;
 
 class UserTest extends \Codeception\Test\Unit
 {
     public function testFindUserById()
     {
-        verify($user = User::findIdentity(100))->notEmpty();
-        verify($user->username)->equals('admin');
+        verify($user = Admin::findIdentity(1))->notEmpty();
+        verify($user->login_user)->equals('root');
 
         verify(User::findIdentity(999))->empty();
     }
index 2f5389a3fd35a826f6acd9418dc3904e6b6b4cd7..18e16f09561b7d8246605639b5a39855dc0cc1cc 100644 (file)
@@ -4,4 +4,4 @@ $this->registerJS("
     var ADMIN_ID = " . (Yii::$app->user->id ?? 0) . ";
 ", \yii\web\View::POS_BEGIN, 'api2_menu_init_global_vars');
 
-//$this->registerJsFile('/js/site/get_menu.js', ['position' => \yii\web\View::POS_END]);
+$this->registerJsFile('/js/site/get_menu.js', ['position' => \yii\web\View::POS_END]);
index 580ed732e86556ec57f3f3395a210246d679c076..a62770a944c96b41c38842479068edc54b737ede 100644 (file)
Binary files a/erp24/web/favicon.ico and b/erp24/web/favicon.ico differ