]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
Синхронизация окладов
authorVladimir Fomichev <vladimir.fomichev@erp-flowers.ru>
Mon, 10 Nov 2025 14:56:56 +0000 (17:56 +0300)
committerVladimir Fomichev <vladimir.fomichev@erp-flowers.ru>
Mon, 10 Nov 2025 14:56:56 +0000 (17:56 +0300)
14 files changed:
erp24/controllers/crud/EmployeePaymentController.php
erp24/controllers/crud/EmployeePositionController.php
erp24/migrations/m251201_120000_replace_salary_with_monthly_and_daily_in_employee_position.php [new file with mode: 0644]
erp24/migrations/m251201_120001_add_employee_position_id_to_employee_payment.php [new file with mode: 0644]
erp24/records/Admin.php
erp24/records/EmployeePayment.php
erp24/records/EmployeePosition.php
erp24/services/SalarySyncService.php [new file with mode: 0644]
erp24/views/crud/employee-payment/_form.php
erp24/views/crud/employee-payment/index.php
erp24/views/crud/employee-payment/view.php
erp24/views/crud/employee-position/_form.php
erp24/views/crud/employee-position/index.php
erp24/views/crud/employee-position/view.php

index 475217c6273c4ba4fdf78fbf35b523fb2e3a1041..dfa58d4cd6e16f9eca062bcdfc3a9d6af6c889bc 100755 (executable)
@@ -9,6 +9,7 @@ use yii\helpers\ArrayHelper;
 use yii_app\records\Admin;
 use yii_app\records\EmployeePayment;
 use yii_app\forms\EmployeePaymentSearch;
+use yii_app\services\SalarySyncService;
 use yii\web\Controller;
 use yii\web\NotFoundHttpException;
 use yii\filters\VerbFilter;
@@ -31,7 +32,7 @@ class EmployeePaymentController extends Controller
                     'rules' => [
                         [
                             'allow' => true,
-                            'actions' => ['create', 'view', 'index', 'update', 'delete'],
+                            'actions' => ['create', 'view', 'index', 'update', 'delete', 'sync-salaries'],
                             'roles' => ['menu/crud/employee-payment/', 'employee-paymentEdit'],
                         ],
                     ],
@@ -212,6 +213,24 @@ class EmployeePaymentController extends Controller
         return $this->redirect(['view', 'id' => $adminId]);
     }
 
+    /**
+     * Синхронизирует оклады для всех сотрудников с заполненным employee_position_id
+     * @return \yii\web\Response
+     */
+    public function actionSyncSalaries()
+    {
+        $syncService = new SalarySyncService();
+        $result = $syncService->syncAllEmployeesFromPositions();
+
+        if ($result['success']) {
+            Yii::$app->session->setFlash('success', $result['message']);
+        } else {
+            Yii::$app->session->setFlash('error', $result['message']);
+        }
+
+        return $this->redirect(['index']);
+    }
+
 
     /**
      * Finds the EmployeePayment model based on its primary key value.
index 75a1db778e12ab7a020c0f0de76ceb22c11c92ae..fd77e82245c86000020b37c73210a5f06ac7dd41 100755 (executable)
@@ -4,9 +4,11 @@ namespace yii_app\controllers\crud;
 
 use yii_app\records\EmployeePosition;
 use yii_app\records\EmployeePositionSearch;
+use yii_app\services\SalarySyncService;
 use yii\web\Controller;
 use yii\web\NotFoundHttpException;
 use yii\filters\VerbFilter;
+use Yii;
 
 /**
  * EmployeePositionController implements the CRUD actions for EmployeePosition model.
@@ -116,6 +118,24 @@ class EmployeePositionController extends Controller
         return $this->redirect(['index']);
     }
 
+    /**
+     * Синхронизирует оклады для всех сотрудников с заполненным employee_position_id
+     * @return \yii\web\Response
+     */
+    public function actionSyncSalaries()
+    {
+        $syncService = new SalarySyncService();
+        $result = $syncService->syncAllEmployeesFromPositions();
+
+        if ($result['success']) {
+            Yii::$app->session->setFlash('success', $result['message']);
+        } else {
+            Yii::$app->session->setFlash('error', $result['message']);
+        }
+
+        return $this->redirect(['index']);
+    }
+
     /**
      * Finds the EmployeePosition model based on its primary key value.
      * If the model is not found, a 404 HTTP exception will be thrown.
diff --git a/erp24/migrations/m251201_120000_replace_salary_with_monthly_and_daily_in_employee_position.php b/erp24/migrations/m251201_120000_replace_salary_with_monthly_and_daily_in_employee_position.php
new file mode 100644 (file)
index 0000000..8a812f8
--- /dev/null
@@ -0,0 +1,61 @@
+<?php
+
+use yii\db\Migration;
+
+class m251201_120000_replace_salary_with_monthly_and_daily_in_employee_position extends Migration
+{
+    const TABLE_NAME = 'erp24.employee_position';
+
+    /**
+     * {@inheritdoc}
+     */
+    public function safeUp()
+    {
+        $tableSchema = $this->db->getTableSchema(self::TABLE_NAME);
+
+        // Добавляем поле monthly_salary
+        if (isset($tableSchema) && !isset($tableSchema->columns['monthly_salary'])) {
+            $this->addColumn(self::TABLE_NAME, 'monthly_salary', $this->decimal(10, 2)->null()->comment('Месячный оклад'));
+        }
+
+        // Добавляем поле daily_payment
+        if (isset($tableSchema) && !isset($tableSchema->columns['daily_payment'])) {
+            $this->addColumn(self::TABLE_NAME, 'daily_payment', $this->decimal(10, 2)->null()->comment('Подневная оплата'));
+        }
+
+        // Переносим данные из salary в monthly_salary, если salary существует
+        if (isset($tableSchema) && isset($tableSchema->columns['salary'])) {
+            $this->execute("UPDATE " . self::TABLE_NAME . " SET monthly_salary = salary WHERE salary IS NOT NULL AND monthly_salary IS NULL");
+            
+            // Удаляем старое поле salary
+            $this->dropColumn(self::TABLE_NAME, 'salary');
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function safeDown()
+    {
+        $tableSchema = $this->db->getTableSchema(self::TABLE_NAME);
+
+        // Восстанавливаем поле salary
+        if (isset($tableSchema) && !isset($tableSchema->columns['salary'])) {
+            $this->addColumn(self::TABLE_NAME, 'salary', $this->integer()->null()->comment('Зарплата'));
+            
+            // Переносим данные обратно из monthly_salary в salary
+            $this->execute("UPDATE " . self::TABLE_NAME . " SET salary = CAST(monthly_salary AS INTEGER) WHERE monthly_salary IS NOT NULL");
+        }
+
+        // Удаляем поле daily_payment
+        if (isset($tableSchema) && isset($tableSchema->columns['daily_payment'])) {
+            $this->dropColumn(self::TABLE_NAME, 'daily_payment');
+        }
+
+        // Удаляем поле monthly_salary
+        if (isset($tableSchema) && isset($tableSchema->columns['monthly_salary'])) {
+            $this->dropColumn(self::TABLE_NAME, 'monthly_salary');
+        }
+    }
+}
+
diff --git a/erp24/migrations/m251201_120001_add_employee_position_id_to_employee_payment.php b/erp24/migrations/m251201_120001_add_employee_position_id_to_employee_payment.php
new file mode 100644 (file)
index 0000000..9191d17
--- /dev/null
@@ -0,0 +1,63 @@
+<?php
+
+use yii\db\Migration;
+
+class m251201_120001_add_employee_position_id_to_employee_payment extends Migration
+{
+    const TABLE_NAME = 'erp24.employee_payment';
+
+    /**
+     * {@inheritdoc}
+     */
+    public function safeUp()
+    {
+        $tableSchema = $this->db->getTableSchema(self::TABLE_NAME);
+
+        // Добавляем поле employee_position_id
+        if (isset($tableSchema) && !isset($tableSchema->columns['employee_position_id'])) {
+            $this->addColumn(self::TABLE_NAME, 'employee_position_id', $this->integer()->null()->comment('ID должности из employee_position'));
+            
+            // Добавляем внешний ключ
+            $this->addForeignKey(
+                'fk-employee_payment-employee_position_id',
+                self::TABLE_NAME,
+                'employee_position_id',
+                'erp24.employee_position',
+                'id',
+                'SET NULL',
+                'CASCADE'
+            );
+            
+            // Добавляем индекс для улучшения производительности
+            $this->createIndex(
+                'idx-employee_payment-employee_position_id',
+                self::TABLE_NAME,
+                'employee_position_id'
+            );
+        }
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function safeDown()
+    {
+        $tableSchema = $this->db->getTableSchema(self::TABLE_NAME);
+
+        // Удаляем индекс
+        if (isset($tableSchema) && isset($tableSchema->columns['employee_position_id'])) {
+            $this->dropIndex('idx-employee_payment-employee_position_id', self::TABLE_NAME);
+        }
+
+        // Удаляем внешний ключ
+        if (isset($tableSchema) && isset($tableSchema->columns['employee_position_id'])) {
+            $this->dropForeignKey('fk-employee_payment-employee_position_id', self::TABLE_NAME);
+        }
+
+        // Удаляем поле employee_position_id
+        if (isset($tableSchema) && isset($tableSchema->columns['employee_position_id'])) {
+            $this->dropColumn(self::TABLE_NAME, 'employee_position_id');
+        }
+    }
+}
+
index bd108177e987697418a0e1f614959100c835f44d..d493b0860dcd961d01b7fc12594fde42adbe7b74 100755 (executable)
@@ -11,10 +11,12 @@ use yii\db\Expression;
 use yii\helpers\ArrayHelper;
 use yii\web\IdentityInterface;
 use yii_app\api3\core\validators\PhoneValidator;
+use yii_app\services\SalarySyncService;
 
 /**
  * Class Admin
  * @property AdminGroup $adminGroup
+ * @property EmployeePosition $employeePosition
  * @property int id
  * @property int $group_id
  * @property string $name
@@ -543,6 +545,17 @@ class Admin extends ActiveRecord implements IdentityInterface
             ->via('positionStatus');
     }
 
+    /**
+     * Gets query for [[EmployeePosition]].
+     * Прямая связь через employee_position_id
+     *
+     * @return \yii\db\ActiveQuery
+     */
+    public function getEmployeePosition()
+    {
+        return $this->hasOne(EmployeePosition::class, ['id' => 'employee_position_id']);
+    }
+
     public function getSkillStatus()
     {
         return $this->hasMany(EmployeeSkillStatus::class, ['admin_id' => 'id']);
@@ -752,4 +765,24 @@ class Admin extends ActiveRecord implements IdentityInterface
 
         return false;
     }
+
+    /**
+     * После сохранения проверяем, изменилась ли должность, и создаем запись EmployeePayment
+     */
+    public function afterSave($insert, $changedAttributes)
+    {
+        parent::afterSave($insert, $changedAttributes);
+
+        // Проверяем, изменилось ли поле employee_position_id
+        if (array_key_exists('employee_position_id', $changedAttributes) || $insert) {
+            $oldPositionId = $changedAttributes['employee_position_id'] ?? null;
+            $newPositionId = $this->employee_position_id;
+
+            // Если должность была добавлена или изменена
+            if ($newPositionId && ($oldPositionId != $newPositionId || $insert)) {
+                $syncService = new SalarySyncService();
+                $syncService->createPaymentFromPosition($this->id);
+            }
+        }
+    }
 }
index b519b3cc7c7bc9c5158a0b7df4a544eb9157169c..04c57f9ae83b9ed030ef8f859351a85da07d8276 100755 (executable)
@@ -11,6 +11,7 @@ use yii\helpers\ArrayHelper;
  * @property int $id
  * @property int $admin_id Сотрудник
  * @property int|null $admin_group_id Должность сотрудника
+ * @property int|null $employee_position_id ID должности из employee_position
  * @property string|null $date Дата начала действия правила
  * @property float $monthly_salary Месячный оклад
  * @property float $daily_payment Подневная оплата
@@ -18,6 +19,7 @@ use yii\helpers\ArrayHelper;
  *
  * @property Admin $admin
  * @property AdminGroup $adminGroup
+ * @property EmployeePosition $employeePosition
  * @property Admin $creator
  */
 class EmployeePayment extends \yii\db\ActiveRecord
@@ -37,10 +39,11 @@ class EmployeePayment extends \yii\db\ActiveRecord
     {
         return [
             [['admin_id', 'monthly_salary', 'daily_payment'], 'required'],
-            [['admin_id', 'admin_group_id', 'creator_id'], 'integer'],
+            [['admin_id', 'admin_group_id', 'employee_position_id', 'creator_id'], 'integer'],
             [['date'], 'date', 'format' => 'php:Y-m-d'],
             [['monthly_salary', 'daily_payment'], 'compare', 'compareValue' => 0, 'operator' => '>', 'type' => 'number', 'message' => 'Значение {attribute} должно быть больше нуля.'],
             [['admin_group_id'], 'exist', 'skipOnError' => true, 'targetClass' => AdminGroup::class, 'targetAttribute' => ['admin_group_id' => 'id']],
+            [['employee_position_id'], 'exist', 'skipOnError' => true, 'targetClass' => EmployeePosition::class, 'targetAttribute' => ['employee_position_id' => 'id']],
             [['admin_id'], 'exist', 'skipOnError' => true, 'targetClass' => Admin::class, 'targetAttribute' => ['admin_id' => 'id']],
             [['creator_id'], 'exist', 'skipOnError' => true, 'targetClass' => Admin::class, 'targetAttribute' => ['creator_id' => 'id']],
             [['daily_payment'], 'validateDailyPayment'],
@@ -91,9 +94,13 @@ class EmployeePayment extends \yii\db\ActiveRecord
     public function beforeValidate()
     {
         if (get_called_class() === self::class) {
-            $this->creator_id = $_SESSION['admin_id'];
+            $this->creator_id = $_SESSION['admin_id'] ?? null;
             if ($this->admin) {
                 $this->admin_group_id = $this->admin->group_id;
+                // Автоматически заполняем employee_position_id из Admin, если не заполнено
+                if (!$this->employee_position_id && $this->admin->employee_position_id) {
+                    $this->employee_position_id = $this->admin->employee_position_id;
+                }
             }
         }
         return parent::beforeValidate();
@@ -108,6 +115,7 @@ class EmployeePayment extends \yii\db\ActiveRecord
             'id' => 'ID',
             'admin_id' => 'Сотрудник',
             'admin_group_id' => 'Должность сотрудника',
+            'employee_position_id' => 'Должность',
             'date' => 'Дата начала действия правила',
             'monthly_salary' => 'Месячный оклад',
             'daily_payment' => 'Подневная оплата',
@@ -135,6 +143,16 @@ class EmployeePayment extends \yii\db\ActiveRecord
         return $this->hasOne(AdminGroup::class, ['id' => 'admin_group_id']);
     }
 
+    /**
+     * Gets query for [[EmployeePosition]].
+     *
+     * @return \yii\db\ActiveQuery
+     */
+    public function getEmployeePosition()
+    {
+        return $this->hasOne(EmployeePosition::class, ['id' => 'employee_position_id']);
+    }
+
     /**
      * Gets query for [[Creator]].
      *
index 404e4c58293bae0cb2d0e078f7aefbb080dcb953..3697ab15724163e21a60a878e8d3c97b5cc69510 100755 (executable)
@@ -7,6 +7,7 @@ use yii\helpers\ArrayHelper;
 use yii\behaviors\TimestampBehavior;
 use yii\behaviors\BlameableBehavior;
 use yii_app\records\EmployeePayment;
+use yii_app\services\SalarySyncService;
 
 /**
  * This is the model class for table "employee_position".
@@ -15,13 +16,17 @@ use yii_app\records\EmployeePayment;
  * @property string $name
  * @property int $next_position_id
  * @property int $posit
- * @property int $salary
+ * @property float|null $monthly_salary Месячный оклад
+ * @property float|null $daily_payment Подневная оплата
  * @property string $alias
  * @property int $group_id
  * @property string $created_at
  * @property int $created_by
  * @property string $updated_at
  * @property int $updated_by
+ *
+ * @property AdminGroup $adminGroup
+ * @property EmployeePayment[] $employeePayments
  */
 class EmployeePosition extends \yii\db\ActiveRecord
 {
@@ -63,9 +68,11 @@ class EmployeePosition extends \yii\db\ActiveRecord
         return [
             [['name'], 'required'],
             [['name', 'alias'], 'string', 'max' => 255],
-            [['next_position_id', 'posit', 'salary', 'group_id', 'created_by', 'updated_by'], 'integer'],
+            [['next_position_id', 'posit', 'group_id', 'created_by', 'updated_by'], 'integer'],
+            [['monthly_salary', 'daily_payment'], 'number'],
             [['created_at', 'updated_at'], 'safe'],
-            [['salary', 'alias', 'group_id', 'created_at', 'created_by', 'updated_at', 'updated_by'], 'default', 'value' => null],
+            [['monthly_salary', 'daily_payment', 'alias', 'group_id', 'created_at', 'created_by', 'updated_at', 'updated_by'], 'default', 'value' => null],
+            [['daily_payment'], 'validateDailyPayment'],
         ];
     }
 
@@ -79,7 +86,8 @@ class EmployeePosition extends \yii\db\ActiveRecord
             'name' => 'Название',
             'next_position_id' => 'Следующая должность',
             'posit' => 'Позиция',
-            'salary' => 'Зарплата',
+            'monthly_salary' => 'Месячный оклад',
+            'daily_payment' => 'Подневная оплата',
             'alias' => 'Алиас',
             'group_id' => 'Группа',
             'created_at' => 'Дата создания',
@@ -89,6 +97,16 @@ class EmployeePosition extends \yii\db\ActiveRecord
         ];
     }
 
+    /**
+     * Валидация для daily_payment, чтобы он не был больше monthly_salary
+     */
+    public function validateDailyPayment($attribute, $params, $validator)
+    {
+        if ($this->daily_payment !== null && $this->monthly_salary !== null && $this->daily_payment > $this->monthly_salary) {
+            $this->addError($attribute, 'Подневная оплата не может быть больше месячного оклада.');
+        }
+    }
+
     public static function getAllIdName()
     {
         $result = [];
@@ -114,4 +132,32 @@ class EmployeePosition extends \yii\db\ActiveRecord
         return $this->hasOne(\yii_app\records\AdminGroup::class, ['id' => 'group_id']);
     }
 
+    /**
+     * Gets query for [[EmployeePayments]].
+     *
+     * @return \yii\db\ActiveQuery
+     */
+    public function getEmployeePayments()
+    {
+        return $this->hasMany(EmployeePayment::class, ['employee_position_id' => 'id']);
+    }
+
+    /**
+     * После сохранения проверяем, изменился ли оклад, и синхронизируем для всех сотрудников с этой должностью
+     */
+    public function afterSave($insert, $changedAttributes)
+    {
+        parent::afterSave($insert, $changedAttributes);
+
+        // Проверяем, изменились ли оклады (monthly_salary или daily_payment)
+        $salaryChanged = array_key_exists('monthly_salary', $changedAttributes) || 
+                        array_key_exists('daily_payment', $changedAttributes);
+
+        // Если оклады изменились и они заполнены, синхронизируем для всех сотрудников
+        if ($salaryChanged && $this->monthly_salary !== null && $this->daily_payment !== null) {
+            $syncService = new SalarySyncService();
+            $creatorId = $this->updated_by ?? $_SESSION['admin_id'] ?? null;
+            $syncService->syncEmployeesByPosition($this->id, $creatorId);
+        }
+    }
 }
diff --git a/erp24/services/SalarySyncService.php b/erp24/services/SalarySyncService.php
new file mode 100644 (file)
index 0000000..ab3a5b3
--- /dev/null
@@ -0,0 +1,242 @@
+<?php
+
+namespace yii_app\services;
+
+use Yii;
+use yii_app\records\Admin;
+use yii_app\records\AdminGroup;
+use yii_app\records\EmployeePayment;
+use yii_app\records\EmployeePosition;
+
+/**
+ * Сервис для синхронизации окладов между EmployeePosition и EmployeePayment
+ */
+class SalarySyncService
+{
+    /**
+     * Синхронизирует оклады для всех сотрудников с заполненным employee_position_id
+     * Создает записи EmployeePayment на основе окладов из EmployeePosition
+     *
+     * @param int|null $creatorId ID пользователя, выполняющего синхронизацию
+     * @return array ['success' => bool, 'message' => string, 'created' => int, 'skipped' => int]
+     */
+    public function syncAllEmployeesFromPositions($creatorId = null): array
+    {
+        if ($creatorId === null) {
+            $creatorId = $_SESSION['admin_id'] ?? null;
+        }
+
+        if (!$creatorId) {
+            return [
+                'success' => false,
+                'message' => 'Не указан ID пользователя для создания записей',
+                'created' => 0,
+                'skipped' => 0
+            ];
+        }
+
+        $created = 0;
+        $skipped = 0;
+        $errors = [];
+
+        // Получаем всех сотрудников с заполненным employee_position_id, не уволенных
+        $admins = Admin::find()
+            ->where(['IS NOT', 'employee_position_id', null])
+            ->andWhere(['!=', 'group_id', AdminGroup::GROUP_FIRED])
+            ->all();
+
+        foreach ($admins as $admin) {
+            $position = EmployeePosition::findOne($admin->employee_position_id);
+            
+            if (!$position) {
+                $skipped++;
+                continue;
+            }
+
+            // Проверяем, заполнены ли оклады для должности
+            if ($position->monthly_salary === null || $position->daily_payment === null) {
+                $skipped++;
+                continue;
+            }
+
+            // Проверяем, есть ли уже запись EmployeePayment для этого сотрудника
+            $existingPayment = EmployeePayment::find()
+                ->where(['admin_id' => $admin->id])
+                ->orderBy(['date' => SORT_DESC])
+                ->one();
+
+            // Если запись уже есть, создаем новую с текущей датой
+            $date = date('Y-m-d');
+
+            // Проверяем, нет ли уже записи на эту дату
+            $paymentOnDate = EmployeePayment::find()
+                ->where(['admin_id' => $admin->id, 'date' => $date])
+                ->one();
+
+            if ($paymentOnDate) {
+                $skipped++;
+                continue;
+            }
+
+            // Создаем новую запись
+            $payment = new EmployeePayment();
+            $payment->admin_id = $admin->id;
+            $payment->admin_group_id = $admin->group_id;
+            $payment->employee_position_id = $position->id;
+            $payment->monthly_salary = $position->monthly_salary;
+            $payment->daily_payment = $position->daily_payment;
+            $payment->date = $date;
+            $payment->creator_id = $creatorId;
+
+            if ($payment->save()) {
+                $created++;
+            } else {
+                $skipped++;
+                $errors[] = "Ошибка для сотрудника {$admin->name} (ID: {$admin->id}): " . implode(', ', $payment->getFirstErrors());
+            }
+        }
+
+        $message = "Синхронизация завершена. Создано записей: {$created}, пропущено: {$skipped}";
+        if (!empty($errors)) {
+            $message .= ". Ошибки: " . implode('; ', array_slice($errors, 0, 5));
+        }
+
+        return [
+            'success' => true,
+            'message' => $message,
+            'created' => $created,
+            'skipped' => $skipped,
+            'errors' => $errors
+        ];
+    }
+
+    /**
+     * Создает запись EmployeePayment для сотрудника на основе его должности
+     *
+     * @param int $adminId ID сотрудника
+     * @param int|null $creatorId ID пользователя, создающего запись
+     * @param string|null $date Дата записи (по умолчанию текущая дата)
+     * @return EmployeePayment|null Созданная запись или null в случае ошибки
+     */
+    public function createPaymentFromPosition($adminId, $creatorId = null, $date = null): ?EmployeePayment
+    {
+        if ($creatorId === null) {
+            $creatorId = $_SESSION['admin_id'] ?? null;
+        }
+
+        if (!$creatorId) {
+            return null;
+        }
+
+        if ($date === null) {
+            $date = date('Y-m-d');
+        }
+
+        $admin = Admin::findOne($adminId);
+        if (!$admin || !$admin->employee_position_id) {
+            return null;
+        }
+
+        $position = EmployeePosition::findOne($admin->employee_position_id);
+        if (!$position || $position->monthly_salary === null || $position->daily_payment === null) {
+            return null;
+        }
+
+        // Проверяем, нет ли уже записи на эту дату
+        $existingPayment = EmployeePayment::find()
+            ->where(['admin_id' => $adminId, 'date' => $date])
+            ->one();
+
+        if ($existingPayment) {
+            return null;
+        }
+
+        $payment = new EmployeePayment();
+        $payment->admin_id = $adminId;
+        $payment->admin_group_id = $admin->group_id;
+        $payment->employee_position_id = $position->id;
+        $payment->monthly_salary = $position->monthly_salary;
+        $payment->daily_payment = $position->daily_payment;
+        $payment->date = $date;
+        $payment->creator_id = $creatorId;
+
+        if ($payment->save()) {
+            return $payment;
+        }
+
+        return null;
+    }
+
+    /**
+     * Синхронизирует оклады для всех сотрудников с указанной должностью
+     * Используется при изменении оклада должности
+     *
+     * @param int $positionId ID должности
+     * @param int|null $creatorId ID пользователя, изменившего оклад
+     * @return array ['success' => bool, 'message' => string, 'created' => int]
+     */
+    public function syncEmployeesByPosition($positionId, $creatorId = null): array
+    {
+        if ($creatorId === null) {
+            $creatorId = $_SESSION['admin_id'] ?? null;
+        }
+
+        if (!$creatorId) {
+            return [
+                'success' => false,
+                'message' => 'Не указан ID пользователя для создания записей',
+                'created' => 0
+            ];
+        }
+
+        $position = EmployeePosition::findOne($positionId);
+        if (!$position || $position->monthly_salary === null || $position->daily_payment === null) {
+            return [
+                'success' => false,
+                'message' => 'Должность не найдена или оклады не заполнены',
+                'created' => 0
+            ];
+        }
+
+        $created = 0;
+        $date = date('Y-m-d');
+
+        // Получаем всех сотрудников с этой должностью, не уволенных
+        $admins = Admin::find()
+            ->where(['employee_position_id' => $positionId])
+            ->andWhere(['!=', 'group_id', AdminGroup::GROUP_FIRED])
+            ->all();
+
+        foreach ($admins as $admin) {
+            // Проверяем, нет ли уже записи на эту дату
+            $existingPayment = EmployeePayment::find()
+                ->where(['admin_id' => $admin->id, 'date' => $date])
+                ->one();
+
+            if ($existingPayment) {
+                continue;
+            }
+
+            // Создаем новую запись
+            $payment = new EmployeePayment();
+            $payment->admin_id = $admin->id;
+            $payment->admin_group_id = $admin->group_id;
+            $payment->employee_position_id = $position->id;
+            $payment->monthly_salary = $position->monthly_salary;
+            $payment->daily_payment = $position->daily_payment;
+            $payment->date = $date;
+            $payment->creator_id = $creatorId;
+
+            if ($payment->save()) {
+                $created++;
+            }
+        }
+
+        return [
+            'success' => true,
+            'message' => "Синхронизация завершена. Создано записей: {$created}",
+            'created' => $created
+        ];
+    }
+}
+
index 49c2ecb1251a88cc155a5360e66111535a714ec3..a901ad3ba87eec2789800a49eef453191964a8cf 100755 (executable)
@@ -34,8 +34,17 @@ use yii\widgets\ActiveForm;
     <?php endif; ?>
 
     <?= $form->field($model, 'date')->textInput(['type' => 'date']) ?>
-    <?= $form->field($model, 'monthly_salary')->textInput(['maxlength' => true]) ?>
-    <?= $form->field($model, 'daily_payment')->textInput(['maxlength' => true]) ?>
+    
+    <?php if ($model->employeePosition): ?>
+        <?= $form->field($model, 'employee_position_id')->textInput([
+            'value' => $model->employeePosition->name,
+            'readonly' => true,
+            'disabled' => true,
+        ])->label('Должность (из справочника)') ?>
+    <?php endif; ?>
+    
+    <?= $form->field($model, 'monthly_salary')->textInput(['maxlength' => true, 'type' => 'number', 'step' => '0.01']) ?>
+    <?= $form->field($model, 'daily_payment')->textInput(['maxlength' => true, 'type' => 'number', 'step' => '0.01']) ?>
 
     <div class="form-group">
         <?= Html::submitButton('Сохранить', ['class' => 'btn btn-success']) ?>
index ee7fd12541ba377d2a5ce9d83665fc4e3dde11cc..a619e2cc5aaaf0d2fe1bf8d6813015a3c26c2eec 100755 (executable)
@@ -6,6 +6,7 @@ use yii\grid\GridView;
 use yii\grid\ActionColumn;
 use yii\widgets\Pjax;
 use yii_app\records\EmployeePayment;
+use Yii;
 
 /* @var $this yii\web\View */
 /* @var $searchModel yii_app\forms\EmployeePaymentSearch */
@@ -20,6 +21,13 @@ $this->params['breadcrumbs'][] = $this->title;
 
     <?php if (Yii::$app->user->identity && Yii::$app->user->identity->hasPermission('employee-paymentEdit')): ?>
         <?= Html::a('Добавить нового сотрудника', ['create'], ['class' => 'btn btn-success mb-4']) ?>
+        <?= Html::a('Синхронизировать оклады', ['sync-salaries'], [
+            'class' => 'btn btn-primary mb-4',
+            'data' => [
+                'confirm' => 'Вы уверены, что хотите синхронизировать оклады для всех сотрудников с заполненной должностью?',
+                'method' => 'post',
+            ],
+        ]) ?>
     <?php endif; ?>
 
     <?php Pjax::begin(); ?>
@@ -49,7 +57,22 @@ $this->params['breadcrumbs'][] = $this->title;
                 }
             ],
 
-            'adminGroup.name:text:Должность',
+            'adminGroup.name:text:Должность(group_name)',
+            [
+                'attribute' => 'admin_id',
+                'label' => 'Должность/группа(group_id)',
+                'value' => function($model) {
+                    return $model->admin ? \yii_app\records\AdminGroup::find()->select('name')
+                        ->where(['id' => $model->admin->group_id])->scalar() : null;
+                }
+            ],
+            [
+                'attribute' => 'employee_position_id',
+                'label' => 'Должность (из справочника)',
+                'value' => function($model) {
+                    return $model->employeePosition ? $model->employeePosition->name : null;
+                }
+            ],
            [
             'attribute' => 'date',
             'format' => ['date', 'php:d.m.Y'],
index a2cf5b8a851720bf8e6ecd9da1a7bccf4fae8f26..fec40617c234b1a527ff4c994e5808dee1e963f9 100755 (executable)
@@ -4,6 +4,7 @@ use yii\helpers\Html;
 use yii\grid\GridView;
 use yii\widgets\DetailView;
 use yii_app\records\EmployeePayment;
+use Yii;
 
 /* @var $this yii\web\View */
 /* @var $admin yii_app\records\Admin */
@@ -33,7 +34,14 @@ $this->params['breadcrumbs'][] = $this->title;
         'attributes' => [
             'id:text:Admin ID',
             'name:text:Сотрудник',
-            'adminGroup.name:text:Должность',
+            'adminGroup.name:text:Должность/группа',
+            [
+                'attribute' => 'employee_position_id',
+                'label' => 'Должность (из справочника)',
+                'value' => function($admin) {
+                    return $admin->employeePosition ? $admin->employeePosition->name : null;
+                }
+            ],
         ],
     ]) ?>
 
index 840f2a3f9b98f5e719f39bd52020814379cc5544..84750f3fe8d36a1aa237589cb55f290df396766e 100755 (executable)
@@ -25,7 +25,9 @@ function printBlock($title, $block) {
 
     <?php printBlock('Алиас', $form->field($model, 'alias')->textInput(['maxlength' => true])->label(false)) ?>
 
-    <?php printBlock('Зарплата', $form->field($model, 'salary')->textInput(['type' => 'number'])->label(false)) ?>
+    <?php printBlock('Месячный оклад', $form->field($model, 'monthly_salary')->textInput(['type' => 'number', 'step' => '0.01'])->label(false)) ?>
+
+    <?php printBlock('Подневная оплата', $form->field($model, 'daily_payment')->textInput(['type' => 'number', 'step' => '0.01'])->label(false)) ?>
 
     <?php printBlock('Группа', $form->field($model, 'group_id')->dropDownList(\yii_app\records\AdminGroup::getAllIdName(), ['prompt' => 'Выберите группу'])->label(false)) ?>
 
index a6679d04cb7985b5ee1b839345e4413b97586d95..e9234a0c86e2d8a0fbaf7ac498b0befe9f298566 100755 (executable)
@@ -19,6 +19,13 @@ $this->params['breadcrumbs'][] = $this->title;
 
     <p>
         <?= Html::a('Создать позицию работника', ['create'], ['class' => 'btn btn-success']) ?>
+        <?= Html::a('Синхронизировать оклады', ['sync-salaries'], [
+            'class' => 'btn btn-primary',
+            'data' => [
+                'confirm' => 'Вы уверены, что хотите синхронизировать оклады для всех сотрудников с заполненной должностью?',
+                'method' => 'post',
+            ],
+        ]) ?>
     </p>
 
     <?php //echo $this->render('_search', ['model' => $searchModel]); ?>
@@ -38,7 +45,8 @@ $this->params['breadcrumbs'][] = $this->title;
                 }
             ],
             'alias',
-            'salary',
+            'monthly_salary',
+            'daily_payment',
             [
                 'attribute' => 'group_id',
                 'value' => function ($model) {
index 0e49380482547eaf729432bed571f28d8ee45dfa..89b6ee7c5a93c6f3efa86b65ec7bd231abb86dbe 100644 (file)
@@ -39,7 +39,8 @@ $this->params['breadcrumbs'][] = $this->title;
             'id',
             'name',
             'alias',
-            'salary',
+            'monthly_salary',
+            'daily_payment',
             [
                 'attribute' => 'group_id',
                 'value' => function ($model) {