Преглед изворни кода

Merge branch 'master' into feature/2234-withdrawal-mod

# Conflicts:
#	common/models/forms/AdminAddUserForm.php
theo пре 2 година
родитељ
комит
0331a16f06
100 измењених фајлова са 6199 додато и 874 уклоњено
  1. 1 1
      backendApi/modules/v1/controllers/ArticleController.php
  2. 59 28
      backendApi/modules/v1/controllers/CalcController.php
  3. 16 5
      backendApi/modules/v1/models/lists/log/UserLoginList.php
  4. 49 49
      backendEle/src/views/bonus/period.vue
  5. 1 1
      common/components/ActiveRecord.php
  6. 1 1
      common/components/SwooleAsyncTimer.php
  7. 1 0
      common/config/bootstrap.php
  8. 47 0
      common/config/i18n.php
  9. 21 4
      common/config/main.php
  10. 4 0
      common/config/params.php
  11. 2 2
      common/helpers/Excel.php
  12. 1 1
      common/helpers/bonus/Calc/BaseBusiness.php
  13. 68 82
      common/helpers/bonus/Calc/CalcConsole.php
  14. 1 0
      common/helpers/bonus/Calc/PushBaseDataToCalc.php
  15. 4 3
      common/helpers/user/Cash.php
  16. 7 2
      common/helpers/user/Info.php
  17. 4 3
      common/libs/logging/login/UserLogin.php
  18. 306 0
      common/messages/en-US/app.php
  19. 310 0
      common/messages/zh-CN/app.php
  20. 5 0
      common/models/ApproachOrder.php
  21. 6 6
      common/models/Article.php
  22. 4 4
      common/models/ArticleCategory.php
  23. 72 0
      common/models/CalcOperation.php
  24. 3 0
      common/models/LogUserLogin.php
  25. 11 11
      common/models/OpenBank.php
  26. 5 0
      common/models/Order.php
  27. 5 0
      common/models/OrderGoods.php
  28. 14 0
      common/models/Period.php
  29. 1 0
      common/models/Region.php
  30. 1 1
      common/models/ShopGoods.php
  31. 85 85
      common/models/User.php
  32. 0 1
      common/models/forms/AdminAddUserForm.php
  33. 17 15
      common/models/forms/ApproachDeclarationForm.php
  34. 11 9
      common/models/forms/ApproachDeclarationLoopForm.php
  35. 8 7
      common/models/forms/ApproachDeclarationUpgradeForm.php
  36. 47 48
      common/models/forms/ApproachOrderForm.php
  37. 9 8
      common/models/forms/BaApproachDeclarationLoopForm.php
  38. 38 38
      common/models/forms/BaApproachOrderForm.php
  39. 8 7
      common/models/forms/BaDeclarationLoopForm.php
  40. 3 2
      common/models/forms/BaUserForm.php
  41. 16 15
      common/models/forms/DeclarationForm.php
  42. 9 8
      common/models/forms/DeclarationLoopForm.php
  43. 10 9
      common/models/forms/DeclarationUpgradeForm.php
  44. 3 0
      common/models/forms/LogUserLoginForm.php
  45. 88 93
      common/models/forms/OrderForm.php
  46. 5 5
      common/models/forms/PeriodForm.php
  47. 3 2
      common/models/forms/TransferForm.php
  48. 4 3
      common/models/forms/UploadForm.php
  49. 10 9
      common/models/forms/UserConfigForm.php
  50. 5 4
      common/models/forms/UserForm.php
  51. 3 2
      common/models/forms/WithdrawForm.php
  52. 9 1
      console/controllers/CalcController.php
  53. 1 1
      frontendApi/config/menu.php
  54. 116 0
      frontendApi/config/menuV2.php
  55. 1 0
      frontendApi/config/params.php
  56. 10 4
      frontendApi/modules/v1/controllers/ArticleController.php
  57. 20 15
      frontendApi/modules/v1/controllers/AtlasController.php
  58. 22 20
      frontendApi/modules/v1/controllers/BaController.php
  59. 15 7
      frontendApi/modules/v1/controllers/BaseController.php
  60. 62 44
      frontendApi/modules/v1/controllers/BonusController.php
  61. 19 18
      frontendApi/modules/v1/controllers/ConfigController.php
  62. 8 1
      frontendApi/modules/v1/controllers/DashboardController.php
  63. 34 25
      frontendApi/modules/v1/controllers/FinanceController.php
  64. 10 8
      frontendApi/modules/v1/controllers/OauthController.php
  65. 383 97
      frontendApi/modules/v1/controllers/ShopController.php
  66. 51 6
      frontendApi/modules/v1/controllers/SiteController.php
  67. 63 31
      frontendApi/modules/v1/controllers/UserController.php
  68. 23 22
      frontendApi/modules/v1/models/LoginForm.php
  69. 4 0
      frontendEle/src/views/layout/layout.vue
  70. 15 0
      node_modules/.package-lock.json
  71. 988 0
      node_modules/moment/CHANGELOG.md
  72. 22 0
      node_modules/moment/LICENSE
  73. 55 0
      node_modules/moment/README.md
  74. 71 0
      node_modules/moment/dist/locale/af.js
  75. 156 0
      node_modules/moment/dist/locale/ar-dz.js
  76. 55 0
      node_modules/moment/dist/locale/ar-kw.js
  77. 171 0
      node_modules/moment/dist/locale/ar-ly.js
  78. 56 0
      node_modules/moment/dist/locale/ar-ma.js
  79. 105 0
      node_modules/moment/dist/locale/ar-sa.js
  80. 55 0
      node_modules/moment/dist/locale/ar-tn.js
  81. 189 0
      node_modules/moment/dist/locale/ar.js
  82. 102 0
      node_modules/moment/dist/locale/az.js
  83. 142 0
      node_modules/moment/dist/locale/be.js
  84. 87 0
      node_modules/moment/dist/locale/bg.js
  85. 52 0
      node_modules/moment/dist/locale/bm.js
  86. 129 0
      node_modules/moment/dist/locale/bn-bd.js
  87. 119 0
      node_modules/moment/dist/locale/bn.js
  88. 124 0
      node_modules/moment/dist/locale/bo.js
  89. 168 0
      node_modules/moment/dist/locale/br.js
  90. 150 0
      node_modules/moment/dist/locale/bs.js
  91. 100 0
      node_modules/moment/dist/locale/ca.js
  92. 180 0
      node_modules/moment/dist/locale/cs.js
  93. 63 0
      node_modules/moment/dist/locale/cv.js
  94. 98 0
      node_modules/moment/dist/locale/cy.js
  95. 53 0
      node_modules/moment/dist/locale/da.js
  96. 79 0
      node_modules/moment/dist/locale/de-at.js
  97. 78 0
      node_modules/moment/dist/locale/de-ch.js
  98. 78 0
      node_modules/moment/dist/locale/de.js
  99. 90 0
      node_modules/moment/dist/locale/dv.js
  100. 106 0
      node_modules/moment/dist/locale/el.js

+ 1 - 1
backendApi/modules/v1/controllers/ArticleController.php

@@ -210,7 +210,7 @@ class ArticleController extends BaseController
             $formModel->file = UploadedFile::getInstanceByName('file');
             $formModel->token = \Yii::$app->request->request('uploadToken');;
             if($formModel->file && $uploader = $formModel->upload()){
-                return static::notice($uploader->URL);
+                return static::notice($uploader->FILE_NAME);
             } else {
                 return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
             }

+ 59 - 28
backendApi/modules/v1/controllers/CalcController.php

@@ -15,6 +15,7 @@ use common\models\CalcRecord;
 use common\models\FlowBonus;
 use common\models\forms\PeriodForm;
 use common\models\Period;
+use common\models\CalcOperation;
 use Yii;
 
 /**
@@ -31,30 +32,34 @@ class CalcController extends BaseController
         return $behaviors;
     }
 
-    public function actionAutoPerf() {
-        $periodNum = \Yii::$app->request->get('periodNum');
-        if (!$periodNum) {
-            return static::notice('期数不存在', 400);
-        }
-        if (Period::isPreparing($periodNum)) {
-            return static::notice('预计算正在进行中请稍后', 400);
-        }
-        //设置自动计算标识
-        Period::updatePeriodIsAutoExec($periodNum, Period::MANUAL_EXEC_CALC);
-        //记录开始计算的时间
-        Period::updateAll(['START_EXEC_TIME' => time()], ['PERIOD_NUM' => $periodNum]);
-
-        $formModel = new PeriodForm();
-        $formModel->scenario = 'autoPerf';
-
-        if ($formModel->load(Yii::$app->request->get(), '') && $formModel->autoPerf()) {
-            //设置预计算进行中标识
-            Period::updatePeriodIsPreparing($periodNum, Period::IS_PREPARING);
-            return static::notice('预计算已开始,请等待');
-        } else {
-            return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
-        }
-    }
+//    public function actionAutoPerf() {
+//        $periodNum = \Yii::$app->request->get('periodNum');
+//        if (!$periodNum) {
+//            return static::notice('期数不存在', 400);
+//        }
+//        if (Period::isPreparing($periodNum)) {
+//            return static::notice('预计算正在进行中请稍后', 400);
+//        }
+//        //设置自动计算标识
+//        Period::updatePeriodIsAutoExec($periodNum, Period::MANUAL_EXEC_CALC);
+//        //记录开始计算的时间
+//        $calc_id = uniqid('perf_');
+//        CalcOperation::record($calc_id, $periodNum);
+//        Period::updateAll(['START_EXEC_TIME' => time(), 'CALC_ID' => $calc_id], ['PERIOD_NUM' => $periodNum]);
+//
+//        $formModel = new PeriodForm();
+//        $formModel->scenario = 'autoPerf';
+//
+//        if ($formModel->load(Yii::$app->request->get(), '') && $formModel->autoPerf()) {
+//            //设置预计算进行中标识
+//            Period::updatePeriodIsPreparing($periodNum, Period::IS_PREPARING);
+//            //设置redis开关
+//            Yii::$app->cache->set('isPreparing', 1, 3600);
+//            return static::notice('预计算已开始,请等待');
+//        } else {
+//            return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
+//        }
+//    }
 
     /**自动计算 对应界面按钮
      * @return mixed
@@ -69,20 +74,24 @@ class CalcController extends BaseController
         if (Period::isProcessing($periodNum)) {
             return static::notice('有操作正在进行中请稍后', 400);
         }
-        if (!Period::checkClose($periodNum)) { // 如果未封期
-            return static::notice('没有封期不能计算', 400);
-        }
+//        if (!Period::checkClose($periodNum)) { // 如果未封期
+//            return static::notice('没有封期不能计算', 400);
+//        }
         //设置计算进行中标识
         Period::updatePeriodIsProcessing($periodNum, Period::IS_PROCESSING);
         //设置自动计算标识
         Period::updatePeriodIsAutoExec($periodNum, Period::AUTO_EXEC_CALC);
         //记录开始计算的时间
-        Period::updateAll(['START_EXEC_TIME' => time()], ['PERIOD_NUM' => $periodNum]);
+        $calc_id = uniqid('calc_');
+        CalcOperation::record($calc_id, $periodNum);
+        Period::updateAll(['START_EXEC_TIME' => time(), 'CALC_ID' => $calc_id], ['PERIOD_NUM' => $periodNum]);
 
         $formModel           = new PeriodForm();
         $formModel->scenario = 'perf';
 
         if ($formModel->load(Yii::$app->request->get(), '') && $formModel->autoExec()) {
+            //设置redis开关
+            Yii::$app->cache->set('isCalcing', 1, 3600);
             return static::notice('自动计算已开始,请等待');
         } else {
             return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
@@ -107,11 +116,17 @@ class CalcController extends BaseController
         Period::updatePeriodIsAutoExec($periodNum, Period::MANUAL_EXEC_CALC);
         //设置计算进行中标识
         Period::updatePeriodIsProcessing($periodNum, Period::IS_PROCESSING);
+        //记录开始计算的时间
+        $calc_id = uniqid('manu_');
+        CalcOperation::record($calc_id, $periodNum);
+        Period::updateAll(['START_EXEC_TIME' => time(), 'CALC_ID' => $calc_id], ['PERIOD_NUM' => $periodNum]);
 
         $formModel           = new PeriodForm();
         $formModel->scenario = 'perf';
 
         if ($formModel->load(Yii::$app->request->get(), '') && $formModel->generatePerfOrder()) {
+            //设置redis开关
+            Yii::$app->cache->set('isCalcing', 1, 3600);
             return static::notice('业绩单已开始生成,请等待');
         } else {
             Period::updatePeriodIsProcessing($periodNum, Period::NOT_PROCESSING);
@@ -174,6 +189,10 @@ class CalcController extends BaseController
         $period = \Yii::$app->$db->createCommand('SELECT * FROM AR_PERIOD where PERIOD_NUM=:PERIOD_NUM')
             ->bindValue(':PERIOD_NUM', $periodNum)
             ->queryOne();
+        $businessPeriod = Period::find()->where(['PERIOD_NUM' => $periodNum])->asArray()->one();
+        if (!$period['CALC_ID'] || $period['CALC_ID'] != $businessPeriod['CALC_ID']) {
+            return static::notice('CALC ID不符', 400);
+        }
 
         if (empty($period)) {
             Period::updatePeriodIsProcessing($periodNum, Period::NOT_PROCESSING);
@@ -206,6 +225,10 @@ class CalcController extends BaseController
         $period = \Yii::$app->$db->createCommand('SELECT * FROM AR_PERIOD where PERIOD_NUM=:PERIOD_NUM')
             ->bindValue(':PERIOD_NUM', $periodNum)
             ->queryOne();
+        $businessPeriod = Period::find()->where(['PERIOD_NUM' => $periodNum])->asArray()->one();
+        if (!$period['CALC_ID'] || $period['CALC_ID'] != $businessPeriod['CALC_ID']) {
+            return static::notice('CALC ID不符', 400);
+        }
 
         if (empty($period)) {
             return static::notice('计算系统中的第' . $periodNum . '期的业绩期信息不存在');
@@ -245,6 +268,10 @@ class CalcController extends BaseController
         $period = \Yii::$app->$db->createCommand('SELECT * FROM AR_PERIOD where PERIOD_NUM=:PERIOD_NUM')
             ->bindValue(':PERIOD_NUM', $periodNum)
             ->queryOne();
+        $businessPeriod = Period::find()->where(['PERIOD_NUM' => $periodNum])->asArray()->one();
+        if (!$period['CALC_ID'] || $period['CALC_ID'] != $businessPeriod['CALC_ID']) {
+            return static::notice('CALC ID不符', 400);
+        }
 
         $formModel           = new PeriodForm();
         $formModel->scenario = 'calc';
@@ -282,6 +309,10 @@ class CalcController extends BaseController
         $period = \Yii::$app->$db->createCommand('SELECT * FROM AR_PERIOD where PERIOD_NUM=:PERIOD_NUM')
             ->bindValue(':PERIOD_NUM', $periodNum)
             ->queryOne();
+        $businessPeriod = Period::find()->where(['PERIOD_NUM' => $periodNum])->asArray()->one();
+        if (!$period['CALC_ID'] || $period['CALC_ID'] != $businessPeriod['CALC_ID']) {
+            return static::notice('CALC ID不符', 400);
+        }
 
         if (empty($period)) {
             return static::notice('计算系统中的第' . $periodNum . '期的业绩期信息不存在');

+ 16 - 5
backendApi/modules/v1/models/lists/log/UserLoginList.php

@@ -37,6 +37,7 @@ class UserLoginList extends \common\libs\dataList\DataList implements DataListIn
                 'device' => 'device',
                 'user_agent' => 'user_agent',
                 'request_route' => 'request_route',
+                'version' => 'version',
             ]);
             $condition = $filter['condition'];
         }
@@ -72,7 +73,7 @@ class UserLoginList extends \common\libs\dataList\DataList implements DataListIn
                 'opt_obj' => [
                     'header' => 'Operation Object', // 操作对象
                     'headerOther' => [
-                        'width' => '120',
+                        'width' => '130',
                     ],
                     'value' => function($row) {
                         return 'member'; // 会员
@@ -81,7 +82,7 @@ class UserLoginList extends \common\libs\dataList\DataList implements DataListIn
                 'user_name' => [
                     'header' => 'Object Number', // 对象编号
                     'headerOther' => [
-                        'width' => '110',
+                        'width' => '120',
                     ],
                 ],
                 'return_result' => [
@@ -93,13 +94,13 @@ class UserLoginList extends \common\libs\dataList\DataList implements DataListIn
                 'success_times' => [
                     'header' => 'Login Success Times', // 登录成功次数
                     'headerOther' => [
-                        'width' => '110',
+                        'width' => '160',
                     ],
                 ],
                 'fail_times' => [
                     'header' => 'Number Of Login Failures', // 登录失败次数
                     'headerOther' => [
-                        'width' => '110',
+                        'width' => '190',
                     ],
                 ],
                 'created_at' => [
@@ -111,7 +112,10 @@ class UserLoginList extends \common\libs\dataList\DataList implements DataListIn
                     },
                     'headerOther' => ['width' => '180'],
                 ],
-                'period_num' => 'Number Of Periods', // 期数
+                'period_num' => [
+                    'header' => 'Number Of Periods', // 期数
+                    'headerOther' => ['width' => '180'],
+                    ],
                 'ip' => [
                     'header' => 'IP Address', // IP地址
                     'headerOther' => [
@@ -136,6 +140,12 @@ class UserLoginList extends \common\libs\dataList\DataList implements DataListIn
                         'width' => '250',
                     ],
                 ],
+                'version' => [
+                    'header' => 'Version', // 版本
+                    'headerOther' => [
+                        'width' => '100',
+                    ],
+                ],
             ];
         }
         return $this->columns;
@@ -160,6 +170,7 @@ class UserLoginList extends \common\libs\dataList\DataList implements DataListIn
                 'device' => ['isUserTable'=>false, 'name'=>'Client'],//客户端
                 'user_agent' => ['isUserTable'=>false, 'name'=>'Operating System'],//操作系统
                 'request_route' => ['isUserTable'=>false, 'name'=>'Request Path'],//请求路径
+                'version' => ['isUserTable'=>false, 'name'=>'Version'],//版本
             ];
         }
         return $this->filterTypes;

+ 49 - 49
backendEle/src/views/bonus/period.vue

@@ -7,12 +7,12 @@
       </div>
       <el-table :data="tableData" stripe style="width: 100%;" @selection-change="handleSelectionChange"
                 :height="tool.getTableHeight()">
-        <el-table-column prop="PERIOD_NUM" label="Number of periods"><!-- 期数 -->
+        <el-table-column prop="PERIOD_NUM" label="Period No." width="100"><!-- 期数 -->
           <template slot-scope="scope">
             <el-tag type="" size="small" class="no-border">{{scope.row.PERIOD_NUM}}</el-tag>
           </template>
         </el-table-column>
-        <el-table-column label="Bonus Month" width="100"><!-- 所在结算月 -->
+        <el-table-column label="Bonus Month" width="110"><!-- 所在结算月 -->
           <template slot-scope="scope">
             <el-tag type="warning" size="small" class="no-border">{{scope.row.CALC_YEAR}}-{{scope.row.CALC_MONTH}}
             </el-tag>
@@ -29,55 +29,55 @@
             {{getWatTime(scope.row.CLOSED_AT)}}
           </template>
         </el-table-column>
-        <el-table-column label="Performance sheet progress" width="90"><!-- 业绩单进度 -->
-          <template slot-scope="scope">
-            <el-progress type="circle" :percentage="Number.parseInt(percentList['PERF_PERCENT'][scope.row.ID])"
-                         :width="50" :stroke-width="3"></el-progress>
-          </template>
-        </el-table-column>
-        <el-table-column label="Time of generating performance sheet" width="210"><!-- 生成业绩单时间 -->
+<!--        <el-table-column label="Performance sheet progress" width="90">&lt;!&ndash; 业绩单进度 &ndash;&gt;-->
+<!--          <template slot-scope="scope">-->
+<!--            <el-progress type="circle" :percentage="Number.parseInt(percentList['PERF_PERCENT'][scope.row.ID])"-->
+<!--                         :width="50" :stroke-width="3"></el-progress>-->
+<!--          </template>-->
+<!--        </el-table-column>-->
+        <el-table-column label="Time of generating performance sheet" width="230"><!-- 生成业绩单时间 -->
           <template slot-scope="scope">
             <!-- 开始 -->start:{{getWatTime(scope.row.PERF_STARTED_AT)}}<br>
             <!-- 完成 -->complete:{{getWatTime(scope.row.PERFED_AT)}}
           </template>
         </el-table-column>
-        <el-table-column label="Settlement progress" width="80"><!-- 结算进度 -->
-          <template slot-scope="scope">
-            <el-progress type="circle" :percentage="Number.parseInt(percentList['CALC_PERCENT'][scope.row.ID])"
-                         :width="50" :stroke-width="3"></el-progress>
-          </template>
-        </el-table-column>
-        <el-table-column label="Date" width="210"><!-- 结算时间 -->
+<!--        <el-table-column label="Settlement progress" width="80">&lt;!&ndash; 结算进度 &ndash;&gt;-->
+<!--          <template slot-scope="scope">-->
+<!--            <el-progress type="circle" :percentage="Number.parseInt(percentList['CALC_PERCENT'][scope.row.ID])"-->
+<!--                         :width="50" :stroke-width="3"></el-progress>-->
+<!--          </template>-->
+<!--        </el-table-column>-->
+        <el-table-column label="Settlement Date" width="230"><!-- 结算时间 -->
           <template slot-scope="scope">
             <!-- 开始 -->start:{{getWatTime(scope.row.CALCULATE_STARTED_AT)}}<br>
             <!-- 完成 -->complete:{{getWatTime(scope.row.CALCULATED_AT)}}
           </template>
         </el-table-column>
-        <el-table-column label="Network connection progress" width="80"><!-- 挂网进度 -->
-          <template slot-scope="scope">
-            <el-progress type="circle" :percentage="Number.parseInt(percentList['SENT_PERCENT'][scope.row.ID])"
-                         :width="50" :stroke-width="3"></el-progress>
-          </template>
-        </el-table-column>
-        <el-table-column label="Network connection time" width="210"><!-- 挂网时间 -->
+<!--        <el-table-column label="Network connection progress" width="80">&lt;!&ndash; 挂网进度 &ndash;&gt;-->
+<!--          <template slot-scope="scope">-->
+<!--            <el-progress type="circle" :percentage="Number.parseInt(percentList['SENT_PERCENT'][scope.row.ID])"-->
+<!--                         :width="50" :stroke-width="3"></el-progress>-->
+<!--          </template>-->
+<!--        </el-table-column>-->
+        <el-table-column label="Network connection time" width="230"><!-- 挂网时间 -->
           <template slot-scope="scope">
             <!-- 开始 -->start:{{getWatTime(scope.row.SEND_STARTED_AT)}}<br/>
             <!-- 完成 -->complete:{{getWatTime(scope.row.SENT_AT)}}
           </template>
         </el-table-column>
-        <el-table-column fixed="right" label="Action" width="180"><!-- 操作 -->
+        <el-table-column fixed="right" label="Action" width=""><!-- 操作 -->
           <template slot-scope="scope">
-            <el-button v-if="scope.row.IS_CAN_CLOSE && permission.hasPermission(`bonus/close-period`)" class="button" type="primary"
-                       @click.native="autoPerfHandle(scope.row.PERIOD_NUM)" >
-              预计算
-            </el-button>
+<!--            <el-button v-if="scope.row.IS_CAN_CLOSE && permission.hasPermission(`bonus/close-period`)" class="button" type="primary"-->
+<!--                       @click.native="trialCalcHandle(scope.row.PERIOD_NUM)" >-->
+<!--              计算-->
+<!--            </el-button>-->
+            <el-button v-if="scope.row.IS_CAN_CLOSE || scope.row.IS_CAN_PERF" type="primary" class="button"
+                       @click="dialogTableVisible = true;currentPeriod = scope.row.PERIOD_NUM;getDialogData();">
+              操作</el-button>
             <el-button v-if="scope.row.IS_CAN_CLOSE && permission.hasPermission(`bonus/close-period`)" type="primary" class="button"
                        @click.native="closeHandle(scope.row)">
               Closure period<!-- 封期 -->
             </el-button>
-            <el-button v-if="scope.row.IS_CAN_PERF" type="primary" class="button"
-                       @click="dialogTableVisible = true;currentPeriod = scope.row.PERIOD_NUM;getDialogData();">
-              操作</el-button>
             <el-button @click.native="sentHandle(scope.row)" type="primary" class="button"
                               v-if="scope.row.IS_CAN_SENT && permission.hasPermission(`bonus/send-period`)">
               <!-- 挂网 -->spread a net
@@ -88,9 +88,9 @@
               <div class="flex">
                 <div>
                   <el-table :data="dialogData" height="550" v-loading="dialogLoading">
-                    <el-table-column prop="CREATED_AT" label="操作时间" width="180">
+                    <el-table-column prop="CREATED_AT" label="操作时间(WAT)" width="180">
                       <template slot-scope="scope">
-                        {{tool.formatDate(scope.row.CREATED_AT)}}
+                        {{getWatTime(scope.row.CREATED_AT)}}
                       </template>
                     </el-table-column>
                     <el-table-column prop="TEXT" label="日志内容" width="650"></el-table-column>
@@ -373,22 +373,22 @@ export default {
       }).catch(response => {
       })
     },
-    autoPerfHandle (currentPeriod) {
-      this.$confirm('Confirm to calculate bonus data for the current period?', 'Hint', { // '确定对当前期进行生成业绩单操作?', '提示'
-        confirmButtonText: 'confirm', // 确定
-        cancelButtonText: 'cancel', // 取消
-        type: 'warning'
-      }).then(() => {
-        return network.getData(`calc/auto-perf/${currentPeriod}`)
-      }).then(response => {
-        this.$message({
-          message: response,
-          type: 'success'
-        })
-        this.getDialogData(this.currentPage, this.pageSize)
-      }).catch(response => {
-      })
-    },
+    // trialCalcHandle (currentPeriod) {
+    //   this.$confirm('Confirm to calculate bonus data for the current period?', 'Hint', { // '确定对当前期进行生成业绩单操作?', '提示'
+    //     confirmButtonText: 'confirm', // 确定
+    //     cancelButtonText: 'cancel', // 取消
+    //     type: 'warning'
+    //   }).then(() => {
+    //     return network.getData(`calc/trial-calc/${currentPeriod}`)
+    //   }).then(response => {
+    //     this.$message({
+    //       message: response,
+    //       type: 'success'
+    //     })
+    //     this.getDialogData(this.currentPage, this.pageSize)
+    //   }).catch(response => {
+    //   })
+    // },
 
     getDialogData () {
       let vueObj = this

+ 1 - 1
common/components/ActiveRecord.php

@@ -308,7 +308,7 @@ class ActiveRecord extends \yii\db\ActiveRecord {
             'currentPage'=>$pagination->page,
             'totalPages'=>$pagination->pageCount,
             'startNum' => $startNum,
-            'totalCount' => $pagination->totalCount,
+            'totalCount' => intval($pagination->totalCount),
             'pageSize' => $pagination->pageSize,
         ];
     }

+ 1 - 1
common/components/SwooleAsyncTimer.php

@@ -55,7 +55,7 @@ class SwooleAsyncTimer extends SwooleAsyncTimerComponent implements SocketInterf
         CalcConsole::listenCalcPeriod();
         //业务系统预计算相关启动逻辑
         CalcConsole::autoPrePerf();
-        CalcConsole::listenAutoPerfPeriod();
+//        CalcConsole::listenAutoPerfPeriod();
         return true;
     }
 

+ 1 - 0
common/config/bootstrap.php

@@ -9,3 +9,4 @@ Yii::setAlias('@frontendApi', dirname(dirname(__DIR__)) . '/frontendApi');
 Yii::setAlias('@backendApi', dirname(dirname(__DIR__)) . '/backendApi');
 Yii::setAlias('@shopApi', dirname(dirname(__DIR__)) . '/shopApi');
 Yii::setAlias('@console', dirname(dirname(__DIR__)) . '/console');
+Yii::setAlias('@rootPath', dirname(dirname(__DIR__)));

+ 47 - 0
common/config/i18n.php

@@ -0,0 +1,47 @@
+<?php
+/**
+ * Configuration file for 'yii message/extract' command.
+ *
+ * This file is automatically generated by 'yii message/config' command.
+ * It contains parameters for source code messages extraction.
+ * You may modify this file to suit your needs.
+ *
+ * You can use 'yii message/config-template' command to create
+ * template configuration file with detailed description for each parameter.
+ */
+return [
+    'color' => null,
+    'interactive' => true,
+    'help' => false,
+    'silentExitOnException' => null,
+    'sourcePath' => '@rootPath',    // 设置为项目跟地址,检索全项目 bootstrap:(Yii::setAlias('@rootPath', dirname(dirname(__DIR__)));)
+//    'sourcePath' => '@yii',
+    'messagePath' => '@yii/messages',
+    'languages' => ['zh-CN'],   // 设置要转换的语言
+    'translator' => 'Yii::t',
+    'sort' => false,
+    'overwrite' => true,
+    'removeUnused' => false,
+    'markUnused' => true,
+    'except' => [
+        '.svn',
+        '.git',
+        '.gitignore',
+        '.gitkeep',
+        '.hgignore',
+        '.hgkeep',
+        '/messages',
+        '/BaseYii.php',
+    ],
+    'only' => [
+        '*.php',
+    ],
+    'format' => 'php',
+    'db' => 'db',
+    'sourceMessageTable' => '{{%source_message}}',
+    'messageTable' => '{{%message}}',
+    'catalog' => 'messages',
+    'ignoreCategories' => [],
+    'phpFileHeader' => '',
+    'phpDocBlock' => null,
+];

+ 21 - 4
common/config/main.php

@@ -1,16 +1,19 @@
 <?php
 
 // 根据运行环境加载不同的配置文件
-if (YII_ENV_DEV) {
+if (YII_ENV == YII_ENV_DEV) {
     $mainConfig = require_once __DIR__ . '/config-development.php';
-} else if (YII_ENV_TEST) {
+} else if (YII_ENV == YII_ENV_TEST) {
     $mainConfig = require_once __DIR__ . '/config-test.php';
-} else if (YII_ENV_PROD) {
+} else if (YII_ENV == YII_ENV_PROD) {
     $mainConfig = require_once __DIR__ . '/config-product.php';
+} else {
+    $mainConfig = require_once __DIR__ . '/config-development.php';
 }
 
 return [
-    'language' => 'en',
+    'language' => 'zh-CN', // zh-CN 目标语言
+    'sourceLanguage' => 'en-US',
     'timeZone' => 'Africa/Lagos',
     'aliases' => [
         '@bower' => '@vendor/bower-asset',
@@ -81,6 +84,20 @@ return [
                 'db' => [ 'class' => 'yii\log\FileTarget'],
             ],
         ],
+        // 多语言
+        'i18n' => [
+            'translations' => [
+                'app*' => [
+                    'class' => 'yii\i18n\PhpMessageSource',
+                    'basePath' => '@common/messages',
+//                    'sourceLanguage' => 'en-US',
+                    'fileMap' => [
+                        'app' => 'app.php',
+                        'app/error' => 'error.php',
+                    ],
+                ],
+            ],
+        ],
     ],
     'controllerMap' => [
         'swoole_server' => [

+ 4 - 0
common/config/params.php

@@ -359,4 +359,8 @@ return [
         'atUserIds'   => ['rob9muw'],
         'isAtAll'     => false,
     ],
+    // 上传服务地址
+    'localUpload' => [
+        'dns' => $mainConfig['localUpload']
+    ],
 ];

+ 2 - 2
common/helpers/Excel.php

@@ -806,8 +806,8 @@ class Excel extends BaseObject {
             $limit = $limit - 1;
         }
         $fileNameArray = ExcelImport::find()->select('U.FILE_NAME')->from(ExcelImport::tableName() . ' AS ET')->join('LEFT JOIN', Uploads::tableName() . ' AS U', 'ET.UPLOAD_ID=U.ID')->where('ET.ID=:ID', [':ID' => $excelImportId])->asArray()->one();
-//        $filePath = \Yii::getAlias('@common/runtime/uploads/' . $fileNameArray['FILE_NAME']);
-        $filePath = '/ng/Volumes/HDD/workshop/old/ar.upload.ming/files/' . $fileNameArray['FILE_NAME'];
+        $filePath =  \Yii::$app->params['localUpload']['dns'] . $fileNameArray['FILE_NAME'];
+//        $filePath = '/ng-stage/Volumes/HDD/workshop/old/ar.upload.ming/files/' . $fileNameArray['FILE_NAME'];
         if ($startRow > $rowCount) {
             return 0;
         }

+ 1 - 1
common/helpers/bonus/Calc/BaseBusiness.php

@@ -46,7 +46,7 @@ class BaseBusiness
         if (empty($periodNum) || !is_numeric($periodNum)) {
             return 0;
         }
-        $db = self::CALC_DB_NAME;
+        $db = CalcConsole::CALC_DB_NAME;
         //同步字段去掉 IS_CALCULATED, 否则自动拉取的轮询间隔之间奖金计算完成后更新该字段则会认为已经拉取过奖金数据
         $period = \Yii::$app->$db->createCommand("SELECT 
                 PERIOD_NUM,

+ 68 - 82
common/helpers/bonus/Calc/CalcConsole.php

@@ -3,7 +3,9 @@
 namespace common\helpers\bonus\Calc;
 
 
+use common\helpers\Cache;
 use common\helpers\Form;
+use common\models\CalcOperation;
 use common\models\CalcRecord;
 use common\models\forms\PeriodForm;
 use common\models\Period;
@@ -23,6 +25,11 @@ class CalcConsole extends BaseBusiness
      */
     public static function listenCalcPeriod()
     {
+        // 先检查redis的开关
+        $calcingSwitch = Yii::$app->cache->get('isCalcing') ? 1 : 0;
+        if(!$calcingSwitch){
+            return true;
+        }
         $db        = self::CALC_DB_NAME;
         $allPeriod = \Yii::$app->$db->createCommand("SELECT * FROM AR_PERIOD order by PERIOD_NUM desc")->queryAll();
         $period    = [];
@@ -39,25 +46,29 @@ class CalcConsole extends BaseBusiness
         $businessPeriod = Period::find()->where(['PERIOD_NUM' => $period['PERIOD_NUM']])
             ->asArray()->one();
 
-        if (empty($businessPeriod)) {
+        if(!$period['CALC_ID'] || $period['CALC_ID'] != $businessPeriod['CALC_ID']) { // 如果计算ID不符,则退出
             return true;
         }
 
-        if ($businessPeriod['SEND_STARTED_AT'] > 0 || $businessPeriod['IS_SENT'] > 0) {
-            //开始挂网 或者 已挂网 则直接返回
+        if (empty($businessPeriod)) {
             return true;
         }
 
-        if ($businessPeriod['IS_CLOSED']==0) {
-            // 如果没有封期 则直接返回
+        if ($businessPeriod['SEND_STARTED_AT'] > 0 || $businessPeriod['IS_SENT'] > 0) {
+            //开始挂网 或者 已挂网 则直接返回
             return true;
         }
+//        if ($businessPeriod['IS_CLOSED']==0) {
+//            // 如果没有封期 则直接返回
+//            return true;
+//        }
 
         //用户选择是否自动执行
         $autoExec             = $businessPeriod['AUTO_EXEC'] > Period::MANUAL_EXEC_CALC;
         $businessIsCalculated = $businessPeriod['IS_CALCULATED'] == Period::CALCULATE_FINISH;
         $startExecTime        = $businessPeriod['START_EXEC_TIME'] ?? 0;
         $businessIsProcessing = $businessPeriod['IS_PROCESSING'] ?? 0;
+        $isPreparing = $businessPeriod['IS_PREPARING'];
 
         echo $businessPeriod['IS_CALCING'];
         //同步计算进度日志到业务系统
@@ -66,7 +77,7 @@ class CalcConsole extends BaseBusiness
             \Yii::$app->db->createCommand()->update('AR_PERIOD', ['IS_CALCING' => 0], 'PERIOD_NUM=' . $period['PERIOD_NUM'])->execute();
         }
 
-        if (2 == $period['IS_PREPARE'] && 1 == $period['IS_PERFED'] && $autoExec && !$businessIsProcessing) {
+        if (2 == $period['IS_PREPARE'] && 1 == $period['IS_PERFED'] && ($autoExec || $isPreparing) && !$businessIsProcessing) {
             //拉取期业绩
             CalcRecord::record($period['PERIOD_NUM'], '【期业绩】第' . $period['PERIOD_NUM'] . '期的业绩数据已生成');
             CalcRecord::record($period['PERIOD_NUM'], '【期业绩】开始获取第' . $period['PERIOD_NUM'] . '期的期业绩数据');
@@ -90,7 +101,7 @@ class CalcConsole extends BaseBusiness
             return \Yii::$app->$db->createCommand()->update('AR_PERIOD', ['IS_PREPARE' => 3], 'PERIOD_NUM=:PERIOD_NUM', ['PERIOD_NUM' => $period['PERIOD_NUM']])->execute();
         }
 
-        if (4 == $period['IS_PREPARE'] && 1 == $period['IS_CALCULATED'] && $autoExec && !$businessIsProcessing && !$businessIsCalculated) {
+        if (4 == $period['IS_PREPARE'] && 1 == $period['IS_CALCULATED'] && ($autoExec || $isPreparing) && !$businessIsProcessing && !$businessIsCalculated) {
             CalcRecord::record($period['PERIOD_NUM'], '【奖金计算】第' . $period['PERIOD_NUM'] . '期的奖金已计算完成');
             CalcRecord::record($period['PERIOD_NUM'], '【奖金计算】开始拉取第' . $period['PERIOD_NUM'] . '期的奖金数据');
 
@@ -108,12 +119,24 @@ class CalcConsole extends BaseBusiness
                 //流程结束
                 //同步周期表的值到业务系统
                 self::pullPeriodForUpdate($period['PERIOD_NUM']);
+                \Yii::$app->$db->createCommand()->update('AR_PERIOD', ['CALC_ID' => ''], 'PERIOD_NUM=:PERIOD_NUM', ['PERIOD_NUM' => $period['PERIOD_NUM']])->execute();
+                CalcOperation::closeOperation($businessPeriod['CALC_ID']);
                 //拉取计算系统的进度日志
                 self::syncLogRecord($period['PERIOD_NUM'], $db, $startExecTime);
+                //设置计算进行中标识
+                Period::updatePeriodIsPreparing($period['PERIOD_NUM'], Period::NOT_PREPARING);
+                //设置redis开关
+                Yii::$app->cache->set('isCalcing', 0);
             } else {
                 //结束计算状态
                 Period::updatePeriodIsProcessing($period['PERIOD_NUM'], Period::NOT_PROCESSING);
+                \Yii::$app->$db->createCommand()->update('AR_PERIOD', ['CALC_ID' => ''], 'PERIOD_NUM=:PERIOD_NUM', ['PERIOD_NUM' => $period['PERIOD_NUM']])->execute();
+                CalcOperation::closeOperation($businessPeriod['CALC_ID']);
                 CalcRecord::record($period['PERIOD_NUM'], '【奖金计算】第' . $period['PERIOD_NUM'] . '期的奖金数据获取失败,原因:' . $res['msg']);
+                //设置计算进行中标识
+                Period::updatePeriodIsPreparing($period['PERIOD_NUM'], Period::NOT_PREPARING);
+                //设置redis开关
+                Yii::$app->cache->set('isCalcing', 0);
                 return $res;
             }
             return true;
@@ -122,95 +145,58 @@ class CalcConsole extends BaseBusiness
         return true;
     }
 
-    public static function autoPrePerf(){ // 自动预结
+    public static function autoPrePerf(){ // 定时计
         $nowTs = time();
         $currentPeriod = Period::find()->where('START_TIME< :NOW_TIME',['NOW_TIME'=>$nowTs])->where('END_TIME>= :NOW_TIME',['NOW_TIME'=>$nowTs-6])->asArray()->one();
         $periodNum = $currentPeriod['PERIOD_NUM'];
+        $prevPeriodNum = $periodNum - 1;
+        $prevPeriod = Period::find()->where('PERIOD_NUM = :PERIOD_NUM',[':PERIOD_NUM'=>$prevPeriodNum])->asArray()->one();
+        if($prevPeriod['IS_SENT']==0){
+            return;
+        }
         if (
             Period::isPreparing($periodNum)
-            || $currentPeriod['IS_MONTH'] == 0
+//            || $currentPeriod['IS_MONTH'] == 0
         ) {
             return;
         }
-        if($nowTs+86400>$currentPeriod['END_TIME'] ){
-            $nowMin = date("i", $nowTs);
-            $nowSec = date("s", $nowTs);
-            if(($nowMin!=0 || $nowSec >= 5 ) || Period::isPreparing($periodNum)){ // 当不是整点小时,当计算中,不计算
-                return;
-            }
-            print_r($periodNum."该预结算".$nowMin.PHP_EOL.$nowSec);
-            //设置计算进行中标识
-            Period::updatePeriodIsPreparing($periodNum, Period::IS_PREPARING);
-            //设置自动计算标识
-            Period::updatePeriodIsAutoExec($periodNum, Period::MANUAL_EXEC_CALC);
-            //记录开始计算的时间
-            Period::updateAll(['START_EXEC_TIME' => time()], ['PERIOD_NUM' => $periodNum]);
-            $formModel = new PeriodForm();
-            $formModel->scenario = 'autoPerf';
-
-            if($formModel->load(['periodNum'=>$periodNum], '') && $formModel->autoPrePerf()) {
-                echo('自动计算已开始,请等待'.PHP_EOL);
-                CalcRecord::record($periodNum, '【定时计算】第' . $periodNum . '期,定时计算开始');
-            } else {
-                echo('自动计算未开始'.PHP_EOL);
-                CalcRecord::record($periodNum, '【定时计算】第' . $periodNum  . '期,定时计算未开始');
-            }
-            return;
-        }else{
+
+        $nowDate = date("d", $nowTs);
+        $nowHour = date("H", $nowTs);
+        $nowMin = date("i", $nowTs);
+        $nowSec = date("s", $nowTs);
+        if($nowDate >1 && $nowDate <= 15 || (($nowMin!=0 || $nowSec >= 5 ) || Period::isPreparing($periodNum)) ){ // 当不是整点小时,当计算中,不计算
             return;
         }
-    }
 
-    public static function listenAutoPerfPeriod() // 拉取预结算的结果
-    {
-        $db        = self::CALC_DB_NAME;
-        $allPeriod = \Yii::$app->$db->createCommand("SELECT * FROM AR_PERIOD order by PERIOD_NUM desc")->queryAll();
-        $period    = [];
-        foreach ($allPeriod as $v) {
-            if ($v['IS_PREPARE'] > 0) {
-                $period = $v;
-                break;
-            }
-        }
-        if (empty($period)) {
-            return true;
-        }
-        $businessPeriod = Period::find()->where(['PERIOD_NUM' => $period['PERIOD_NUM']])->asArray()->one();
-        $startExecTime = $businessPeriod['START_EXEC_TIME'] ?? 0;
-        if (
-            $businessPeriod['SEND_STARTED_AT'] > 0 || $businessPeriod['IS_SENT'] > 0 // 已挂网
-            || 1 != $businessPeriod['IS_PREPARING'] // 未在预计算中
-            || $period['PERFED_AT'] < $businessPeriod['START_EXEC_TIME'] // 计算服务中的计算时间,早于业务系统开始计算时间,说明是旧的,不拉取
-        ){
-            return false;
+        if (($nowTs + 86400 < $currentPeriod['END_TIME']) && $nowHour > 0) {
+            return;
         }
-        if (
-            2 == $period['IS_PREPARE']
-            && 1 == $period['IS_PERFED']
-        ) {
-            //拉取期业绩
-            CalcRecord::record($period['PERIOD_NUM'], '【期业绩】第' . $period['PERIOD_NUM'] . '期的预计算业绩数据已生成');
-            CalcRecord::record($period['PERIOD_NUM'], '【期业绩】开始获取第' . $period['PERIOD_NUM'] . '期的预计算期业绩数据');
 
-            Period::updatePeriodIsPreparing($period['PERIOD_NUM'], Period::IS_PREPARING);
-            $res = (new PullPerfDataFromCalc($period['PERIOD_NUM']))->start();
-            if (200 == $res['code']) {
-//                Period::updatePeriodIsProcessing($period['PERIOD_NUM'], Period::NOT_PROCESSING);
-                //设置预计算标识
-                Period::updatePeriodIsPreparing($period['PERIOD_NUM'], Period::NOT_PREPARING);
-                CalcRecord::record($period['PERIOD_NUM'], '【期业绩】第' . $period['PERIOD_NUM'] . '期的预计算期业绩数据已获取');
-                self::syncLogRecord($period['PERIOD_NUM'], $db, $startExecTime);
-                return $res;
-            } else {
-                //结束计算状态
-//                Period::updatePeriodIsProcessing($period['PERIOD_NUM'], Period::NOT_PROCESSING);
-                //设置预计算标识
-                Period::updatePeriodIsPreparing($period['PERIOD_NUM'], Period::NOT_PREPARING);
-                CalcRecord::record($period['PERIOD_NUM'], '【期业绩】第' . $period['PERIOD_NUM'] . '期的预计算期业绩数据获取失败,原因:' . $res['msg']);
-                return $res;
-            }
+        print_r($periodNum."该预结算".$nowMin.PHP_EOL.$nowSec);
+
+        //设置计算进行中标识
+        Period::updatePeriodIsPreparing($periodNum, Period::IS_PREPARING);
+        //设置自动计算标识
+        Period::updatePeriodIsAutoExec($periodNum, Period::AUTO_EXEC_CALC);
+        //记录开始计算的时间
+        $calc_id = uniqid('calc_');
+        CalcOperation::record($calc_id, $periodNum);
+        Period::updateAll(['START_EXEC_TIME' => time(), 'CALC_ID' => $calc_id], ['PERIOD_NUM' => $periodNum]);
+        $formModel = new PeriodForm();
+        $formModel->scenario = 'autoPerf';
+
+        if($formModel->load(['periodNum'=>$periodNum], '') && $formModel->autoPrePerf()) {
+            echo('自动计算已开始,请等待'.PHP_EOL);
+            //设置redis开关
+            Yii::$app->cache->set('isCalcing', 1, 3600);
+            CalcRecord::record($periodNum, '【定时计算】第' . $periodNum . '期,定时计算开始');
+        } else {
+            echo('自动计算未开始'.PHP_EOL);
+            CalcRecord::record($periodNum, '【定时计算】第' . $periodNum . '期,定时计算未开始');
         }
-        return true;
+        return;
+
     }
 
     /**

+ 1 - 0
common/helpers/bonus/Calc/PushBaseDataToCalc.php

@@ -276,6 +276,7 @@ class PushBaseDataToCalc extends BaseBusiness
             'CALCULATE_STARTED_AT',
             'CALCULATED_AT',
             'CREATED_AT',
+            'CALC_ID',
         ]],
         //--- 月业绩表 只有当前结算月为季度最终月时才推
         'perfMonth'        => ['separately' => true, 'table' => 'AR_PERF_MONTH', 'field' => [

+ 4 - 3
common/helpers/user/Cash.php

@@ -14,6 +14,7 @@ use common\libs\lock\RedisLock;
 use common\models\FlowWallet;
 use common\models\Period;
 use common\models\UserWallet;
+use Yii;
 use yii\base\Exception;
 use yii\db\Expression;
 
@@ -65,7 +66,7 @@ class Cash {
                 $lockKey = self::CASH_BALANCE_LOCK_KEY . $userId;
                 break;
             default:
-                throw new Exception('流水类型错误');
+                throw new Exception(Yii::t('app', 'cashDoesNotAdequate'));
         }
         if (RedisLock::instance()->lock($lockKey)) {
             // 改变发奖
@@ -77,7 +78,7 @@ class Cash {
                 $oneUserBonusModel->$type += $amount;
                 if ($oneUserBonusModel->$type < 0) {
                     RedisLock::instance()->unlock($lockKey);
-                    throw new Exception('金额不足');
+                    throw new Exception(Yii::t('app', 'cashDoesNotAdequate'));
                 }
                 UserWallet::updateAll($paramData, 'USER_ID=:USER_ID', [':USER_ID' => $userId]);
 
@@ -121,7 +122,7 @@ class Cash {
             unset($flowInsertData);
             RedisLock::instance()->unlock($lockKey);
         } else {
-            throw new Exception('流水产生错误');
+            throw new Exception(Yii::t('app', 'flowCreateError'));
         }
         return true;
     }

+ 7 - 2
common/helpers/user/Info.php

@@ -12,6 +12,7 @@ namespace common\helpers\user;
 use common\components\ActiveQuery;
 use common\components\ActiveRecord;
 use common\helpers\Cache;
+use common\helpers\LoggerTool;
 use common\helpers\Tool;
 use common\models\BaUser;
 use common\models\BaUserInfo;
@@ -24,6 +25,7 @@ use common\models\DecRole;
 use common\models\EmployLevel;
 use common\models\OpenBank;
 use common\models\Region;
+use common\models\StarCrownLevel;
 use common\models\User;
 use common\models\UserBonus;
 use common\models\UserInfo;
@@ -129,6 +131,7 @@ class Info {
         if (!$data['DEC_LV']) $data['DEC_LV'] = DeclarationLevel::getDefaultLevelId();
         if (!$data['LAST_DEC_LV']) $data['LAST_DEC_LV'] = DeclarationLevel::getDefaultLevelId();
         if (!$data['EMP_LV']) $data['EMP_LV'] = EmployLevel::getDefaultLevelId();
+        if (!$data['CROWN_LV']) $data['CROWN_LV'] = StarCrownLevel::getDefaultLevelId();
         return $data;
     }
 
@@ -205,6 +208,7 @@ class Info {
                     if (!$data['DEC_LV']) $data['DEC_LV'] = DeclarationLevel::getDefaultLevelId();
                     if (!$data['LAST_DEC_LV']) $data['LAST_DEC_LV'] = DeclarationLevel::getDefaultLevelId();
                     if (!$data['EMP_LV']) $data['EMP_LV'] = EmployLevel::getDefaultLevelId();
+                    if (!$data['CROWN_LV']) $data['CROWN_LV'] = StarCrownLevel::getDefaultLevelId();
                     if (!$data['HIGHEST_EMP_LV']) $data['HIGHEST_EMP_LV'] = EmployLevel::getDefaultLevelId();
                     return $data;
                 }else{
@@ -245,6 +249,7 @@ class Info {
         $baseInfo['LAST_DEC_LV_NAME'] = Cache::getDecLevelConfig()[$baseInfo['LAST_DEC_LV']]['LEVEL_NAME'];
         $baseInfo['EMP_LV_NAME'] = Cache::getEmpLevelConfig()[$baseInfo['EMP_LV']]['LEVEL_NAME'];
         $baseInfo['HIGHEST_EMP_LV_NAME'] = '';
+        $baseInfo['CROWN_LV_NAME'] = Cache::getStarCrownLevelConfig()[$baseInfo['CROWN_LV']]['LEVEL_NAME'];
         // 获取会员报单级别
         $baseInfo['DEC_ROLE_NAME'] = '无';
         //民族
@@ -611,7 +616,7 @@ class Info {
                 $password = $userName;
                 break;
             default:
-                $password = '123456';
+                $password = '111111';
         }
         return $password;
     }
@@ -715,4 +720,4 @@ class Info {
     public static function getBaBaseUserByUserName($username) {
         return BaUser::findOneAsArray('USER_NAME=:USER_NAME', [':USER_NAME' => $username]);
     }
-}
+}

+ 4 - 3
common/libs/logging/login/UserLogin.php

@@ -22,10 +22,10 @@ class UserLogin {
      * @return LogUserLoginForm
      * @throws \Exception
      */
-    public static function success($userInfo){
+    public static function success($userInfo, $version){
         $returnResult = '';
         $successTimes = intval($userInfo['LOGIN_NUMS']) + 1;
-        $result = self::recorder($userInfo['USER_NAME'], '1', $returnResult, $userInfo['FAIL_NUMS'], $successTimes);
+        $result = self::recorder($userInfo['USER_NAME'], '1', $returnResult, $userInfo['FAIL_NUMS'], $successTimes, $version);
         return $result;
     }
 
@@ -52,7 +52,7 @@ class UserLogin {
      * @return LogUserLoginForm
      * @throws \Exception
      */
-    public static function recorder($account, $optType, $returnResult, $failTimes, $successTimes){
+    public static function recorder($account, $optType, $returnResult, $failTimes, $successTimes, $version = ''){
         $period = Period::instance();
         $periodNum = $period->getNowPeriodNum();
         $form = new LogUserLoginForm([
@@ -67,6 +67,7 @@ class UserLogin {
             'device' => Yii::$app->request->getDevice(),
             'request_route' => Yii::$app->requestedRoute,
             'return_result' => $returnResult,
+            'version' => $version,
         ]);
         if(!$form->add()){
             throw new \Exception(Form::formatErrorsForApi($form->getErrors()));

+ 306 - 0
common/messages/en-US/app.php

@@ -0,0 +1,306 @@
+<?php
+return [
+    # 商城
+    'addressId' => 'Address',
+    'reconsume' => 'Reconsume',
+    'orderCode' => 'Order Code',
+    'orderDetail' => 'Order Detail',
+    'productCode' => 'Product Code',
+    'productName' => 'Product Name',
+    'productPrice' => 'Product Price',
+    'qty' => 'Qty',
+    'taxRate' => 'Tax Rate',
+    'totalTax' => 'Total Tax',
+    'totalAmount' => 'Total Amount',
+    'total' => 'Total',
+    'signature' => 'Signature',
+    'date' => 'Date',
+    'accountBalance' => 'Account Balance',
+    'travelBonus' => 'Travel Bonus',
+    'carFund' => 'Car Fund',
+    'villaFund' => 'Villa Fund',
+    'reconsumeSuccessfully' => '帮会员复消成功',
+    'reconsumeRemark' => '复销备注',
+    'standardProducts' => 'Standard Products',
+    'carFundProducts' => 'Car Fund Products',
+    'villaFundProducts' => 'Villa Fund Products',
+    'travelFundProducts' => 'Travel Fund Products',
+    'insufficientInventory' => 'Insufficient inventory',
+    'soldOut' => 'Sold out',
+    'productsDoesNotExists' => 'Products does not exists!',
+    'cashDoesNotAdequate' => '余额不足,无法购买商品',
+    'exchangePointDoesNotAdequate' => '兑换积分不足,无法购买商品',
+    'travelPointDoesNotAdequate' => '旅游积分不足,无法购买商品',
+    'carFundPointDoesNotAdequate' => '车奖积分不足,无法购买商品',
+    'villaPointDoesNotAdequate' => '房奖积分不足,无法购买商品',
+    'paymentInfoDoesNotExists' => '支付信息不存在',
+    'payAmountNotEqualOrderAmount' => '支付金额与订单金额不符',
+    'orderSn' => 'Order Code',
+    'expressCompany' => 'Express Company',
+    'orderTrackNo' => 'Track Code',
+    'remark' => 'Remark',
+    'orderType' => 'Order Type',
+    'payType' => 'Pay Type',
+    'productID' => 'Product ID',
+    'quantity' => 'Quantity',
+    'repeatSalesMemberNo' => 'UserName',
+    'consigneeNo' => 'Consignee',
+    'acceptMobile' => 'Accept Mobile',
+    'lgaName' => 'Lga Name',
+    'cityName' => 'City Name',
+    'detailAddress' => 'Address',
+    'shippingAddress' => 'shipping Address',
+
+    'orderDoesNotExist' => 'the order does not exist',
+    'orderHasBeenCancelCanNotDeliver ' => '订单已取消不能发货',
+    'orderHasBeenDeleteCanNotDeliver' => '订单已删除不能发货',
+    'orderPayStatusDoesNotSupportRefund' => '订单状态支付状态不支持退款',
+    'orderLogisticsStatusDoesNotSupportRefund' => '订单物流状态不支持退款',
+    'orderPayTypeDoesNotSupportRefund' => '订单支付方式不支持退款',
+    'paymentPasswordError' => 'The payment password is incorrect',
+    'shippingDoesNotExist' => 'the shipping address does not exist',
+    'payTypeError' => '支付方式错误',
+    'orderCanNotHasMoreClassification' => '订单不能包含多种商品分类',
+    'shopGoodClassificationError' => '商品分类错误',
+    'orderStatusTypeError' => '订单状态类型错误',
+    'orderStatusDoesNotChange' => '订单状态没有改变',
+    'orderHasBeenLogisticsStatusDoesNotChangedUnpaid' => '订单已经进入物流状态不能改为未支付',
+    'orderHasBeenInvalidCanNotProcess' => '订单已失效不能处理',
+    'orderCanNotBeenChangedLogistics' => '订单不能单独处理为物流状态',
+    'orderHasBeenFinishedCanNotCancel' => '订单已完成不能取消',
+    'orderHasBeenDeletedCanNotCancel' => '订单已删除不能取消',
+    'orderHasBeenFinishedCanNotDelete' => '订单已完成不能删除',
+    'flowTypeError' => '流水类型错误',
+    'flowCreateError' => '流水产生错误',
+    'doesNotYourSubMemberCanNotReconsume' => '不是您的伞下会员,不能为其复消',
+    'membersResellingBalancePayment' => 'Members reselling balance payment',
+    'membersExchangePointPayment' => '会员复销积分兑换',
+    'orderCanNotContainMultipleProductCategories' => 'Order cannot contain multiple product categories',
+
+
+    # 会员
+    'memberCode' => 'Member Code',
+    'memberName' => 'Member Name',
+    'memberAddress' => 'Member Address',
+    'memberPhone' => 'Member Phone',
+    'bankName' => 'Bank Name',
+    'bankCode' => 'Bank Code',
+    'admin' => 'Admin',
+    'minCharge' => 'Min Charge',
+    'maxCharge' => 'Max Charge',
+    'chargeRate' => 'Charge Rate',
+    'updater' => 'Updater',
+    'userName' => 'USER_NAME',
+    'passwordHash' => 'PASSWORD_HASH',
+    'payPassword' => 'PAY_PASSWORD',
+    'nation' => 'NATION',
+    'realName' => 'REAL_NAME',
+    'IDCard' => 'ID_CARD',
+    'IDType' => 'ID_TYPE',
+    'mobile' => 'MOBILE',
+    'address' => 'ADDRESS',
+    'IDImage' => 'ID_IMAGE',
+    'openBank' => 'OPEN_BANK',
+    'bankAddress' => 'BANK_ADDRESS',
+    'bankNo' => 'BANK_NO',
+    'bankProvince' => 'BANK_PROVINCE',
+    'bankCity' => 'BANK_CITY',
+    'bankCounty' => 'BANK_COUNTY',
+    'spouseName' => 'SPOUSE_NAME',
+    'spouseIDCard' => 'SPOUSE_IDCARD',
+    'decClosed' => 'DEC_CLOSED',
+    'decClosedAt' => 'DEC_CLOSED_AT',
+    'decLv' => 'DEC_LV',
+    'empLv' => 'EMP_LV',
+    'crownLv' => 'CROWN_LV',
+    'province' => 'PROVINCE',
+    'city' => 'CITY',
+    'county' => 'COUNTY',
+    'tel' => 'TEL',
+    'subComID' => 'SUB_COM_ID',
+    'avatar' => 'AVATAR',
+    'isDec' => 'IS_DEC',
+    'isAtlas' => 'IS_ATLAS',
+    'isRecharge' => 'IS_RECHARGE',
+    'DEC_ID' => 'DEC_ID',
+    'birthday' => 'BIRTHDAY',
+    'decRoleID' => 'DEC_ROLE_ID',
+    'periodAt' => 'PERIOD_AT',
+    'decProvince' => 'DEC_PROVINCE',
+    'decCity' => 'DEC_CITY',
+    'decCounty' => 'DEC_COUNTY',
+    'isUnion' => 'IS_UNION',
+    'statusAt' => 'STATUS_AT',
+    'verified' => 'VERIFIED',
+    'verifiedAt' => 'VERIFIED_AT',
+    'allowLogin' => 'ALLOW_LOGIN',
+    'notOperating' => 'NOT_OPERATING',
+    'regFrom' => 'REG_FROM',
+    'IDCardPrefix' => 'ID_CARD_PREFIX',
+    'sex' => 'SEX',
+    'decAccountOld' => 'DEC_ACCOUNT_OLD',
+    'bankUpdatedAt' => 'BANK_UPDATED_AT',
+    'isDirectSeller' => 'IS_DIRECT_SELLER',
+    'decLvUpdatedAt' => 'DEC_LV_UPDATED_AT',
+    'decLvUpdatedPeriod' => 'DEC_LV_UPDATED_PERIOD',
+    'decAddress' => 'DEC_ADDRESS',
+    'decPhone' => 'DEC_PHONE',
+    'guarantor' => 'GUARANTOR',
+    'guarantorName' => 'GUARANTOR_NAME',
+    'partFuncClosed' => 'PART_FUNC_CLOSED',
+    'lastDecLvUpdatedPeriod' => 'LAST_DEC_LV_UPDATED_PERIOD',
+    'userCreator' => 'USER_CREATOR',
+    'userUpdater' => 'USER_UPDATER',
+    'lastDecLvUpdatedAt' => 'LAST_DEC_LV_UPDATED_AT',
+    'decCreatedAt' => 'DEC_CREATED_AT',
+    'partFuncClosedRemark' => 'PART_FUNC_CLOSED_REMARK',
+    'decCreatedPeriod' => 'DEC_CREATED_PERIOD',
+    'passwordChanged' => 'PASSWORD_CHANGED',
+    'subComLeader' => 'SUB_COM_LEADER',
+    'zgUpgradePv' => 'ZG_UPGRADE_PV',
+    'appClientID' => 'APP_CLIENT_ID',
+    'readAgreement' => 'READ_AGREEMENT',
+    'lastDecLv' => 'LAST_DEC_LV',
+    'bonusAppClientID' => 'BONUS_APP_CLIENT_ID',
+    'isFirstOpen' => 'IS_FIRST_OPEN',
+    'isModifyPassword' => 'IS_MODIFY_PASSWORD',
+    'isStudio' => 'IS_STUDIO',
+    'email' => 'EMAIL',
+
+    'fillingUpOfADeficit' => 'filling up of a deficit',
+    'fullPayment' => 'full payment',
+
+    'personalDataModifiedSuccessfully' => 'Personal data modified successfully',
+    'passwordModifiedSuccessfully' => 'Password modified successfully',
+    'theFunctionIsNotAvailable' => 'The function is not available',
+    'inactiveUser' => 'Inactive user. Please contact customer service.',
+    'checkPerformance' => 'Please contact customer service to check performance.',
+    'checkPerformanceOfUpgradedMember' => 'Please contact the customer service personnel to check the performance of upgraded members',
+    'memberNumberExpired' => 'Member number expired',
+    'memberNumberDoesNotConformTo' => 'Member number does not conform to',
+    'failedToGenerateMemberNumber' => 'Failed to generate member number',
+    'memberNumberDoesNotExist' => 'Member number does not exist',
+    'repeatSalesMemberNoDoesNotExist' => 'Repeat sales Member No. does not exist',
+    'memberDoesNotExist' => 'Member does not exist',
+    'memberDoesNotInSamePlacementNetwork' => 'The member is not in the same placement network as the current user',
+    'viewSubMembersMost' => 'View the top 20 sub members of the member at most',
+    'upgradeMethodIncorrect' => 'The upgrade method is incorrect. Please contact the customer service personnel',
+
+    'originalLoginPasswordError' => 'Original login password error',
+    'originalPaymentPasswordError' => 'Original payment password error',
+    'userNameNotExists' => 'Nonexistent user name',
+    'pleaseSelectUpgradeLevel' => 'Please select upgrade level',
+    'totalPVLessThan' => 'Total PV cannot be less than the selected level PV',
+    'totalPvExceedPv' => 'The total PV cannot exceed the PV value of the next level under the selected level',
+    'deliveryTemporarilyNotSupported' => 'Delivery is temporarily not supported in the region. Contact customer service for details',
+    'applicantCashShort' => 'The applicant is short of cash and cannot complete the declaration',
+    'failedToUpgrade' => 'Failed to upgrade for member',
+    'bulkDeclarationNotSames' => 'Bulk declaration member must be the same member',
+    'reportFormatIncorrect' => 'The format of the report data is incorrect',
+    'memberNumberCanNotContainChineseCharacters' => 'Member number cannot contain Chinese characters',
+    'pleaseSelectTheEntryLevel' => 'Please select the entry level',
+    'totalBVCanNotLessThanSelectedBV' => 'The total BV of self selected goods cannot be less than the BV of the selected entry level',
+    'totalBVCanNotLessThanNextSelectedLevelBV' => 'The total BV of self selected goods cannot exceed the BV value of the next level under the selected level',
+    'incorrectEntryType' => 'Incorrect entry type',
+    'MembershipNumberFilledInitialPurchase' => 'Membership number must be filled in for initial purchase',
+    'fillTheInstructorNumberTheMember' => 'For the first purchase, you must fill in the instructor number of the member',
+    'sponsorNumberMustBeFilled' => 'For the first purchase, the Sponsor number of the member must be filled in',
+    'beFilledTheMarketMember' => 'The first purchase must be filled in the market of the member',
+    'pleaseSelectMarket' => 'Please select a market',
+    'stockistDoesNotExist' => 'Stockist does not exist',
+    'newMemberDoesNotExist' => 'New member does not exist',
+    'brandAmbassadorUpgradeError' => 'Brand Ambassador upgrade error',
+    'changeUserStatusError' => 'change user status error',
+    'dataFormatError' => 'Data format error',
+
+
+
+    # 奖金
+    'memberBonus' => 'Member Bonus',
+    'memberEcoin' => 'Member Ecoin',
+    'carPoints' => 'Car Points',
+    'villaPoints' => 'Villa Points',
+    'increase' => 'increase',
+    'reduce' => 'reduce',
+    'cannotViewThisPeriod' => 'Cannot view this period',
+    'bonusRecordDoesNotExists' => 'The bonus record does not exists of this period',
+    'pleaseSelectThePeriod' => 'Please select the period',
+    'withdrawalApplicationHasBeenSubmitted' => 'Withdrawal application has been submitted, please wait for review.',
+    'withdrawHasBeenBacked' => 'Withdraw has been backed',
+    'withdrawDoesNotAllowedOfAuthentication' => '未实名验证无法提现',
+    'withdrawDoesNotAllowedOfSubsidiaryMember' => '附属会员无法提现',
+    'autoWithdrawHasBeenOpened' => '已开启自动提现,如需手动提现请关闭自动提现',
+    'withdrawNotAllowOfDate' => '未到提现日期,请在每月挂网后第一周申请提现',
+    'withdrawAllowOnceOfMonth' => '提现失败,每月只可以提现一次',
+    'withdrawRecordHasNotVerify' => '提现失败,您存在未审核的提现记录',
+    'notionalPoolingAmountFinished' => '归集完成,归集金额',
+    'withdrawDoesNotUploadInvoice' => '该提现记录无法上传发票',
+    'transferDoesNotAllowedOfAuthentication' => '未实名验证无法转账',
+    'transferDoesNotAllowed' => '不允许转账',
+    'transferDoesNotOpen' => '转账功能未启用',
+    'transferTypeDoesNotExists' => '没有可用的转账类型',
+    'rechargeApplicationHasBeenSubmitted' => 'Recharge application has been submitted, please wait for review.',
+
+    # 设置
+    'autoWithdrawHasBeenClosed' => '开启关闭自动提现成功',
+    'closeMessageSendSuccessfully' => '开启关闭复销短信提醒成功',
+    'changeDefaultAddressFailed' => '更新默认地址失败',
+    'addShippingAddressSuccessfully' => '添加收货地址成功',
+    'updateUserConfigFailed' => '更新个人设置失败',
+    'isAutoWithdraw' => '是否自动提现',
+    'allowReconsumeSms' => '开启复销短信通知',
+    'pleaseAuthentication' => '请先实名认证',
+    'systemCloseAutoWithdraw' => '系统关闭了自动提现',
+    'systemCloseReconsumeSMSNotification' => '系统关闭了开启复销短信通知',
+    'openAutoWithdraw' => '开启关闭自动提现',
+    'updateUserSMSExpiredFailed' => '更新个人短信有效期失败',
+    'modeDoesNotExist' => '不存在此方式',
+
+    # 首页
+    'pcOf' => 'PC of ',
+
+    # 财务
+    'statueError' => 'Status error',
+    'notAllowedToTransferToYourself' => 'Not allowed to transfer to yourself',
+    'wrongTransactionType' => 'Wrong transaction type',
+    'isCanWithdrawBack' => '提现退回已关闭',
+    'withdrawRecordDoesNotExist' => '符合退回的提现记录不存在',
+
+
+    # 登录
+    'refreshTokenFailed' => 'refresh token failed',
+    'passwordChangeSucceeded' => 'Password change succeeded',
+    'accountDoesNotExist' => 'The account does not exist',
+    'memberNameOrPasswordIncorrect' => 'The member name or password is incorrect',
+    'abnormalMemberCode' => 'Abnormal member code',
+    'memberNotActivated' => 'Member not activated',
+    'memberHasBeenCancelled' => 'The member has been cancelled',
+    'memberHasBeenBlacklisted' => 'The member has been blacklisted',
+    'memberHasBeenPermanentlySuspended' => 'The member has been permanently suspended',
+    'memberPartOfFunctionClosed' => 'Member part of the function is closed, unable to log in.',
+    'memberAppDeviceInformationUpdateFailed' => 'Member APP device information update failed',
+
+    # 文章
+    'category' => 'Category',
+    'title' => 'Title',
+    'content' => 'Content',
+    'articleDoesNotExist' => 'The article does not exist',
+
+    # 公用
+    'notConnection' => 'Connection not operated for too long',
+    'deleteFailed' => 'failed to delete',
+    'deleteSuccessfully' => 'delete successfully',
+    'quickLoginCanNotOperate' => '快速登录的会员无法进行任何操作',
+    'selectAtLeastOne' => '必须选择一条数据',
+    'failed' => 'Failed',
+    'successfully' => 'Successfully',
+    'createAt' => 'Creation Time',
+    'status' => 'Status',
+    'state' => 'State',
+    'sort' => 'Sort',
+    'illegalRequest' => 'Illegal request',
+    'invalidParameter' => 'Invalid Parameter',
+    'dataDoesNotExists' => 'The data does not exist',
+
+
+];

+ 310 - 0
common/messages/zh-CN/app.php

@@ -0,0 +1,310 @@
+<?php
+return [
+    # 商城
+    'addressId' => '收货地址',
+    'orderCode' => '订单号',
+    'orderDetail' => '订单详情',
+    'productCode' => '商品编号',
+    'productName' => '商品名字',
+    'productPrice' => '商品价格',
+    'qty' => '数量',
+    'taxRate' => '税率',
+    'totalTax' => '数额总计',
+    'totalAmount' => '总金额',
+    'total' => '总计',
+    'signature' => '签名',
+    'date' => '日期',
+    'accountBalance' => '账户余额',
+    'travelBonus' => '旅游积分',
+    'carFund' => '车奖积分',
+    'villaFund' => '房奖积分',
+    'reconsumeSuccessfully' => '帮会员复消成功',
+    'reconsume' => '帮会员复销',
+    'reconsumeRemark' => '复销备注',
+    'standardProducts' => '普通商品列表',
+    'carFundProducts' => '车奖积分商品',
+    'villaFundProducts' => '房奖积分商品',
+    'travelFundProducts' => '旅游积分商品',
+    'insufficientInventory' => '库存不足',
+    'soldOut' => '已下架',
+    'productsDoesNotExists' => '商品不存在',
+    'cashDoesNotAdequate' => '余额不足,无法购买商品',
+    'exchangePointDoesNotAdequate' => '兑换积分不足,无法购买商品',
+    'travelPointDoesNotAdequate' => '旅游积分不足,无法购买商品',
+    'carFundPointDoesNotAdequate' => '车奖积分不足,无法购买商品',
+    'villaPointDoesNotAdequate' => '房奖积分不足,无法购买商品',
+    'paymentInfoDoesNotExists' => '支付信息不存在',
+    'payAmountNotEqualOrderAmount' => '支付金额与订单金额不符',
+
+
+    'orderSn' => '订单号',
+    'expressCompany' => '快递公司',
+    'orderTrackNo' => '快递单号',
+    'remark' => '备注',
+    'orderType' => '订单类型',
+    'payType' => '支付方式',
+    'productID' => '商品ID',
+    'quantity' => '商品数量',
+    'repeatSalesMemberNo' => '复消会员编号',
+    'consignee' => '收货人',
+    'acceptMobile' => '收货电话',
+    'lgaName' => '地域',
+    'cityName' => '城市',
+    'detailAddress' => '详细地址',
+    'shippingAddress' => '收货地址',
+
+    'orderDoesNotExist' => '订单不存在',
+    'orderHasBeenCancelCanNotDeliver ' => '订单已取消不能发货',
+    'orderHasBeenDeleteCanNotDeliver' => '订单已删除不能发货',
+    'orderPayStatusDoesNotSupportRefund' => '订单状态支付状态不支持退款',
+    'orderLogisticsStatusDoesNotSupportRefund' => '订单物流状态不支持退款',
+    'orderPayTypeDoesNotSupportRefund' => '订单支付方式不支持退款',
+    'paymentPasswordError' => '支付密码不正确',
+    'shippingDoesNotExist' => '收货地址不存在',
+    'payTypeError' => '支付方式错误',
+    'orderCanNotHasMoreClassification' => '订单不能包含多种商品分类',
+    'shopGoodClassificationError' => '商品分类错误',
+    'orderStatusTypeError' => '订单状态类型错误',
+    'orderStatusDoesNotChange' => '订单状态没有改变',
+    'orderHasBeenLogisticsStatusDoesNotChangedUnpaid' => '订单已经进入物流状态不能改为未支付',
+    'orderHasBeenInvalidCanNotProcess' => '订单已失效不能处理',
+    'orderCanNotBeenChangedLogistics' => '订单不能单独处理为物流状态',
+    'orderHasBeenFinishedCanNotCancel' => '订单已完成不能取消',
+    'orderHasBeenDeletedCanNotCancel' => '订单已删除不能取消',
+    'orderHasBeenFinishedCanNotDelete' => '订单已完成不能删除',
+    'flowTypeError' => '流水类型错误',
+    'flowCreateError' => '流水产生错误',
+    'doesNotYourSubMemberCanNotReconsume' => '不是您的伞下会员,不能为其复消',
+    'orderCanNotContainMultipleProductCategories' => '订单不能包含多种商品分类',
+    'dataFormatError' => '数据格式错误',
+
+    # 会员
+    'memberCode' => '会员编号',
+    'memberName' => '会员名字',
+    'memberAddress' => '收货地址',
+    'bankName' => '银行名称',
+    'bankCode' => '银行代码',
+    'admin' => '操作人',
+    'minCharge' => '最低手续费',
+    'maxCharge' => '最高手续费',
+    'chargeRate' => '手续费比例',
+    'updater' => '更新人',
+
+    'userName' => '帐号',
+    'passwordHash' => '登录密码',
+    'payPassword' => '支付密码',
+    'nation' => '民族',
+    'realName' => '姓名',
+    'IDCard' => '身份证号',
+    'IDType' => '证件类型',
+    'mobile' => '手机号',
+    'address' => '身份证地址',
+    'IDImage' => '证件图片',
+    'openBank' => '开户行',
+    'bankAddress' => '银行地址',
+    'bankNo' => '银行卡号',
+    'bankProvince' => '银行省份',
+    'bankCity' => '银行城市',
+    'bankCounty' => '银行县区',
+    'spouseName' => '配偶姓名',
+    'spouseIDCard' => '配偶证件号',
+    'decClosed' => '是否关闭报单功能',
+    'decClosedAt' => '关闭时间',
+    'decLv' => '报单级别',
+    'empLv' => '聘级',
+    'crownLv' => '星级',
+    'province' => '省份',
+    'city' => '城市',
+    'county' => '县区',
+    'tel' => '座机',
+    'subComID' => '子公司ID',
+    'avatar' => '头像',
+    'isDec' => '是否为报单中心',
+    'isAtlas' => '是否显示图谱',
+    'isRecharge' => '是否显示充值',
+    'DEC_ID' => '报单中心ID',
+    'birthday' => '生日',
+    'decRoleID' => '报单中心级别',
+    'periodAt' => '期数',
+    'decProvince' => '报单中心所属的省份',
+    'decCity' => '报单中心所属的城市',
+    'decCounty' => '报单中心所属的县区',
+    'isUnion' => '是否为点位合作',
+    'statusAt' => '状态更改时间',
+    'verified' => '是否认证',
+    'verifiedAt' => '认证时间',
+    'allowLogin' => '允许登录',
+    'notOperating' => '不运作',
+    'regFrom' => '注册类型',
+    'IDCardPrefix' => '身份证前缀',
+    'sex' => '性别',
+    'decAccountOld' => '老系统中的报单中心编号',
+    'bankUpdatedAt' => '银行信息更新时间',
+    'isDirectSeller' => '是否为直销员',
+    'decLvUpdatedAt' => '报单级别更新时间',
+    'decLvUpdatedPeriod' => '报单级别更新期数',
+    'decAddress' => '报单中心详细地址',
+    'decPhone' => '报单中心电话',
+    'guarantor' => '担保人编号',
+    'guarantorName' => '担保人姓名',
+    'partFuncClosed' => '部分功能开启',
+    'lastDecLvUpdatedPeriod' => '上次报单级别更新期数',
+    'userCreator' => '创建人',
+    'userUpdater' => '修改人',
+    'lastDecLvUpdatedAt' => '上次报单级别更新时间',
+    'decCreatedAt' => '成为报单中心时间',
+    'partFuncClosedRemark' => '部分功能关闭原因',
+    'decCreatedPeriod' => '成为报单中心期数',
+    'passwordChanged' => '是否修改过密码',
+    'subComLeader' => '是否为分公司领导',
+    'zgUpgradePv' => '增购升级PV',
+    'appClientID' => 'APP设备ID',
+    'readAgreement' => '已读协议',
+    'lastDecLv' => '上次的报单级别',
+    'bonusAppClientID' => '结算APP设备ID',
+    'isFirstOpen' => '首次开通',
+    'isModifyPassword' => '是否修改密码',
+    'isStudio' => '是否是工作室',
+    'email' => '电子邮箱',
+
+
+
+    'fillingUpOfADeficit' => '补差额',
+    'fullPayment' => '全额',
+
+    'personalDataModifiedSuccessfully' => '个人资料修改成功',
+    'passwordModifiedSuccessfully' => '密码修改成功',
+    'theFunctionIsNotAvailable' => '功能暂未开放',
+    'inactiveUser' => '非激活用户,请联系客服',
+    'checkPerformance' => '请联系客服人员核对业绩',
+    'checkPerformanceOfUpgradedMember' => '请联系客服人员核对升级会员业绩',
+    'memberNumberExpired' => '会员编号过期',
+    'memberNumberDoesNotConformTo' => '会员编号不符合',
+    'failedToGenerateMemberNumber' => '会员编号生成失败',
+    'memberNumberDoesNotExist' => '会员编号不存在',
+    'repeatSalesMemberNoDoesNotExist' => '复消会员编号不存在',
+    'memberDoesNotExist' => '会员不存在',
+    'memberDoesNotInSamePlacementNetwork' => '会员与当前用户不再同一安置网络内',
+    'viewSubMembersMost' => '最多查看会员的前20层子会员',
+    'upgradeMethodIncorrect' => '升级方式不正确,请联系客服人员',
+
+    'originalLoginPasswordError' => '原登录密码错误',
+    'originalPaymentPasswordError' => '原支付密码错误',
+    'userNameNotExists' => '不存在的用户名',
+    'pleaseSelectUpgradeLevel' => '请选择升级级别',
+    'totalPVLessThan' => '总PV不能小于所选级别PV',
+    'totalPvExceedPv' => '总PV不能超过已选级别下一个级别的PV值',
+    'deliveryTemporarilyNotSupported' => '地区暂时不支持配送,具体联系客服',
+    'applicantCashShort' => '报单人现金不足,无法完成报单',
+    'failedToUpgrade' => '为会员升级失败',
+    'bulkDeclarationNotSames' => '批量报单会员必须是同一身份证',
+    'reportFormatIncorrect' => '报单数据格式错误',
+    'memberNumberCanNotContainChineseCharacters' => '会员编号中不能含有汉字',
+    'pleaseSelectTheEntryLevel' => '请选择报单级别',
+    'totalBVCanNotLessThanSelectedBV' => '自选商品总BV不能小于所选报单级别BV',
+    'totalBVCanNotLessThanNextSelectedLevelBV' => '自选商品总BV不能超过已选级别下一个级别的BV值',
+    'incorrectEntryType' => '报单类型错误',
+    'MembershipNumberFilledInitialPurchase' => '首购必须填写加入会员编号',
+    'fillTheInstructorNumberTheMember' => '首购必须填写加入会员的指导老师编号',
+    'sponsorNumberMustBeFilled' => '首购必须填写加入会员的开拓人编号',
+    'beFilledTheMarketMember' => '首购必须填写加入会员的市场',
+    'pleaseSelectMarket' => '请选择市场',
+    'stockistDoesNotExist' => '报单中心不存在',
+    'newMemberDoesNotExist' => '新加入的会员不存在',
+    'membersResellingBalancePayment' => '会员复销余额支付',
+    'membersExchangePointPayment' => '会员复销积分兑换',
+    'brandAmbassadorUpgradeError' => 'BA会员升级错误',
+    'changeUserStatusError' => '修改会员状态错误',
+
+
+    # 奖金
+    'memberBonus' => '会员奖金',
+    'memberEcoin' => '会员余额',
+    'carPoints' => '车奖积分',
+    'villaPoints' => '房奖积分',
+    'increase' => '增加',
+    'reduce' => '减少',
+    'cannotViewThisPeriod' => '该期不能查看',
+    'bonusRecordDoesNotExists' => '当期无奖金记录',
+    'pleaseSelectThePeriod' => '请选择期数',
+    'withdrawalApplicationHasBeenSubmitted' => '提现申请已提交,请等待审核',
+    'withdrawHasBeenBacked' => '提现已退回',
+    'withdrawDoesNotAllowedOfAuthentication' => '未实名验证无法提现',
+    'withdrawDoesNotAllowedOfSubsidiaryMember' => '附属会员无法提现',
+    'autoWithdrawHasBeenOpened' => '已开启自动提现,如需手动提现请关闭自动提现',
+    'withdrawNotAllowOfDate' => '未到提现日期,请在每月挂网后第一周申请提现',
+    'withdrawAllowOnceOfMonth' => '提现失败,每月只可以提现一次',
+    'withdrawRecordHasNotVerify' => '提现失败,您存在未审核的提现记录',
+    'notionalPoolingAmountFinished' => '归集完成,归集金额',
+    'withdrawDoesNotUploadInvoice' => '该提现记录无法上传发票',
+    'transferDoesNotAllowedOfAuthentication' => '未实名验证无法转账',
+    'transferDoesNotAllowed' => '不允许转账',
+    'transferDoesNotOpen' => '转账功能未启用',
+    'transferTypeDoesNotExists' => '没有可用的转账类型',
+    'rechargeApplicationHasBeenSubmitted' => '充值申请已提交,请等待审核',
+
+    # 设置
+    'autoWithdrawHasBeenClosed' => '开启关闭自动提现成功',
+    'closeMessageSendSuccessfully' => '开启关闭复销短信提醒成功',
+    'changeDefaultAddressFailed' => '更新默认地址失败',
+    'addShippingAddressSuccessfully' => '添加收货地址成功',
+    'updateUserConfigFailed' => '更新个人设置失败',
+    'isAutoWithdraw' => '是否自动提现',
+    'allowReconsumeSms' => '开启复销短信通知',
+    'pleaseAuthentication' => '请先实名认证',
+    'systemCloseAutoWithdraw' => '系统关闭了自动提现',
+    'systemCloseReconsumeSMSNotification' => '系统关闭了开启复销短信通知',
+    'openAutoWithdraw' => '开启关闭自动提现',
+    'updateUserSMSExpiredFailed' => '更新个人短信有效期失败',
+    'modeDoesNotExist' => '不存在此方式',
+
+
+    # 首页
+    'pcOf' => 'PC of ',
+
+    # 财务
+    'statueError' => 'Status error',
+    'notAllowedToTransferToYourself' => '不允许向自己转账',
+    'isCanWithdrawBack' => '提现退回已关闭',
+    'withdrawRecordDoesNotExist' => '符合退回的提现记录不存在',
+
+    # 文章
+    'category' => '分类',
+    'title' => '标题',
+    'content' => '内容',
+    'articleDoesNotExist' => '文章不存在',
+
+    # 登录
+    'refreshTokenFailed' => '刷新token失败',
+    'passwordChangeSucceeded' => '密码修改成功',
+    'wrongTransactionType' => '错误的交易类型',
+    'accountDoesNotExist' => '账号不存在',
+    'memberNameOrPasswordIncorrect' => '用户名或密码错误',
+    'abnormalMemberCode' => '会员编号异常',
+    'memberNotActivated' => '会员未激活',
+    'memberHasBeenCancelled' => '会员已被注销',
+    'memberHasBeenBlacklisted' => '会员已被列入黑名单',
+    'memberHasBeenPermanentlySuspended' => '会员已被永久关停',
+    'memberPartOfFunctionClosed' => '会员部分功能关闭,无法登录.',
+    'memberAppDeviceInformationUpdateFailed' => '会员APP设备信息更新失败',
+
+    # 公用
+    'notConnection' => '长时间未操作',
+    'deleteFailed' => '删除失败',
+    'deleteSuccessfully' => '删除成功',
+    'quickLoginCanNotOperate' => '快速登录的会员无法进行任何操作',
+    'selectAtLeastOne' => '必须选择一条数据',
+    'failed' => '失败',
+    'successfully' => '成功',
+    'createAt' => '创建时间',
+    'updateAt' => '更新时间',
+    'deletedAt' => '删除时间',
+    'status' => '状态',
+    'state' => '状态',
+    'sort' => '排序',
+    'deleted' => '是否删除',
+    'illegalRequest' => '非法请求',
+    'invalidParameter' => '无效参数',
+    'dataDoesNotExists' => '数据不存在',
+
+];

+ 5 - 0
common/models/ApproachOrder.php

@@ -136,4 +136,9 @@ class ApproachOrder extends \common\components\ActiveRecord
             'EMAIL' => 'Email'
         ];
     }
+
+    public function getUserByUserId()
+    {
+        return $this->hasOne(User::class, ['ID' => 'USER_ID']);
+    }
 }

+ 6 - 6
common/models/Article.php

@@ -48,12 +48,12 @@ class Article extends \common\components\ActiveRecord
     {
         return [
             'ID' => 'ID',
-            'TITLE' => 'Title', // 标题
-            'CID' => 'Category Type', // 分类
-            'CONTENT' => 'Content', // 内容
-            'STATUS' => 'State', // 状态
-            'Order' => 'Sort', // 排序
-            'CREATED_AT' => 'Creation Time', // 创建时间
+            'TITLE' => Yii::t('app', 'title'),
+            'CID' => Yii::t('app', 'category'),
+            'CONTENT' => Yii::t('app', 'content'),
+            'STATUS' => Yii::t('app', 'state'),
+            'Order' => Yii::t('app', 'sort'),
+            'CREATED_AT' => Yii::t('app', 'createAt'),
         ];
     }
 }

+ 4 - 4
common/models/ArticleCategory.php

@@ -45,10 +45,10 @@ class ArticleCategory extends \common\components\ActiveRecord
     {
         return [
             'ID' => 'ID',
-            'CATE_NAME' => '分类名',
-            'STATUS' => '状态',
-            'SORT' => 'Sort',
-            'CREATED_AT' => '创建时间',
+            'CATE_NAME' => Yii::t('app', 'category'),
+            'STATUS' => Yii::t('app', 'status'),
+            'SORT' => Yii::t('app', 'sort'),
+            'CREATED_AT' => Yii::t('app', 'createAt'),
         ];
     }
 

+ 72 - 0
common/models/CalcOperation.php

@@ -0,0 +1,72 @@
+<?php
+
+namespace common\models;
+
+use Yii;
+
+/**
+ * This is the model class for table "{{%RECHARGE}}".
+ *
+ * @property string $ID
+ * @property string $CALC_ID 单号
+ * @property string $ADMIN_ID 会员ID
+ * @property int $PERIOD_NUM 期数
+ * @property int $START_AT 开始时间
+ * @property int $END_AT 结束时间
+ * @property int $STATUS 状态
+ */
+
+class CalcOperation extends \common\components\ActiveRecord {
+    /**
+     * @inheritdoc
+     */
+    public static function tableName() {
+        return '{{%CALC_OPERATION}}';
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function rules() {
+        return [
+        ];
+    }
+
+    /**
+     * @inheritdoc
+     */
+    public function attributeLabels() {
+        return [
+            'ID' => 'ID',
+            'CALC_ID' => '计算ID',
+            'ADMIN_ID' => '管理员ID',
+            'PERIOD_NUM' => '期数',
+            'START_TIME' => '开始时间',
+            'END_TIME' => '结束时间',
+            'STATUS' => '状态',
+        ];
+    }
+
+    /**
+     * @param $periodNum
+     * @param $text
+     * @return bool
+     * @throws \yii\db\Exception
+     */
+    public static function record($calcId, $periodNum)
+    {
+        self::insertOne([
+            'CALC_ID' => $calcId,
+            'ADMIN_ID' => '',
+            'PERIOD_NUM' => $periodNum,
+            'START_TIME' => time(),
+            'END_TIME' => '',
+            'STATUS' => 0,
+        ]);
+        return true;
+    }
+
+    public static function closeOperation($calcId){
+        self::updateAll(['STATUS' => 1, 'END_TIME' => time()], 'CALC_ID=:CALC_ID', [':CALC_ID' => $calcId]);
+    }
+}

+ 3 - 0
common/models/LogUserLogin.php

@@ -20,6 +20,7 @@ use Yii;
  * @property mixed $created_at
  * @property mixed $user_agent
  * @property mixed $period_num
+ * @property mixed $version
  */
 class LogUserLogin extends MongoActiveRecord
 {
@@ -64,6 +65,7 @@ class LogUserLogin extends MongoActiveRecord
             'device',
             'request_route',
             'return_result',
+            'version'
         ];
     }
 
@@ -99,6 +101,7 @@ class LogUserLogin extends MongoActiveRecord
             'device' => '客户端',
             'request_route' => '请求路径',
             'return_result' => '返回内容',
+            'version' => '版本',
         ];
     }
 

+ 11 - 11
common/models/OpenBank.php

@@ -54,17 +54,17 @@ class OpenBank extends \common\components\ActiveRecord
     {
         return [
             'ID' => 'ID',
-            'BANK_NAME' => '银行名称',
-            'BANK_CODE' => '银行代码',
-            'LIST_ORDER' => '排序',
-            'CREATED_AT' => '创建时间',
-            'UPDATED_AT' => '更新时间',
-            'ADM_NAME' => '操作人',
-            'MIN_CHARGE' => '最低手续费',
-            'MAX_CHARGE' => '最高手续费',
-            'CHARGE_PROP' => '手续费比例',
-            'UPDATER' => '更新人',
-            'STATUS' => '状态',
+            'BANK_NAME' => Yii::t('app', 'bankName'),'银行名称',
+            'BANK_CODE' => Yii::t('app', 'bankCode'),'银行代码',
+            'LIST_ORDER' => Yii::t('app', 'sort'),'排序',
+            'CREATED_AT' => Yii::t('app', 'createAt'),'创建时间',
+            'UPDATED_AT' => Yii::t('app', 'updateAt'),'更新时间',
+            'ADM_NAME' => Yii::t('app', 'admin'),'操作人',
+            'MIN_CHARGE' => Yii::t('app', 'minCharge'),'最低手续费',
+            'MAX_CHARGE' => Yii::t('app', 'maxCharge'),'最高手续费',
+            'CHARGE_PROP' => Yii::t('app', 'chargeRate'),'手续费比例',
+            'UPDATER' => Yii::t('app', 'updater'),'更新人',
+            'STATUS' => Yii::t('app', 'status'),'状态',
         ];
     }
 

+ 5 - 0
common/models/Order.php

@@ -172,4 +172,9 @@ class Order extends \common\components\ActiveRecord
     {
         return $this->hasOne(User::class, ['ID' => 'USER_ID']);
     }
+
+    public function getOrderGoods()
+    {
+        return $this->hasMany(OrderGoods::class, ['SN' => 'ORDER_SN']);
+    }
 }

+ 5 - 0
common/models/OrderGoods.php

@@ -74,4 +74,9 @@ class OrderGoods extends \common\components\ActiveRecord
             'EMAIL' => 'Email'
         ];
     }
+
+    public function getShopGoods()
+    {
+        return $this->hasMany(ShopGoods::class, ['ID' => 'GOODS_ID']);
+    }
 }

+ 14 - 0
common/models/Period.php

@@ -2,6 +2,7 @@
 
 namespace common\models;
 
+use common\helpers\bonus\Calc\CalcConsole;
 use common\helpers\Date;
 use common\helpers\Tool;
 use Yii;
@@ -894,7 +895,20 @@ class Period extends \common\components\ActiveRecord
      */
     public static function isProcessing($periodNum = null): bool
     {
+        $db        = CalcConsole::CALC_DB_NAME;
+        $allPeriod = \Yii::$app->$db->createCommand("SELECT * FROM AR_PERIOD order by PERIOD_NUM desc")->queryAll();
+        $calcPeriod    = [];
+        foreach ($allPeriod as $v) {
+            if ($v['IS_PREPARE'] > 0) {
+                $calcPeriod = $v;
+                break;
+            }
+        }
         $period = static::findOneAsArray(['PERIOD_NUM' => $periodNum]);
+        // 若计算服务中CALC_ID不为空,则不允许计算
+        if ($calcPeriod) {
+            if ($calcPeriod['CALC_ID']) return true;
+        }
         //挂网则拒绝操作,返回true则拒绝操作
         if ($period['IS_SENT'] == self::SEND_FINISH) return true;
         if ($period['IS_PROCESSING'] == self::IS_PROCESSING) return true;

+ 1 - 0
common/models/Region.php

@@ -71,6 +71,7 @@ class Region extends \common\components\ActiveRecord
      */
     public static function getCnName($regionCode){
 //        self::updateToCache();
+        if (!$regionCode) return '';
         $allData = self::getFromCache();
         return $allData[$regionCode]['REGION_NAME'] ?? '';
     }

+ 1 - 1
common/models/ShopGoods.php

@@ -101,7 +101,7 @@ class ShopGoods extends \common\components\ActiveRecord
     const CATEGORY_TYPE = [
         [
             'id' => 1,
-            'name' => 'Common products',//普通商品
+            'name' => 'Standard Products',//普通商品
             'sell_type' => [
                 self::SALE_TYPE[1],
 //                self::SALE_TYPE[3],

+ 85 - 85
common/models/User.php

@@ -135,87 +135,87 @@ class User extends \common\components\ActiveRecord
     {
         return [
             'ID' => 'ID',
-            'USER_NAME' => '帐号',
-            'PASSWORD_HASH' => '登录密码',
-            'PAY_PASSWORD' => '支付密码',
-            'NATION' => '民族',
-            'REAL_NAME' => '姓名',
-            'ID_CARD' => '身份证号',
-            'ID_TYPE' => '证件类型',
-            'MOBILE' => '手机号',
-            'ADDRESS' => '身份证地址',
-            'ID_IMAGE' => '证件图片',
-            'OPEN_BANK' => '开户行',
-            'BANK_ADDRESS' => '银行地址',
-            'BANK_NO' => '银行卡号',
-            'BANK_PROVINCE' => '银行省份',
-            'BANK_CITY' => '银行城市',
-            'BANK_COUNTY' => '银行县区',
-            'SPOUSE_NAME' => '配偶姓名',
-            'SPOUSE_IDCARD' => '配偶证件号',
-            'CREATED_AT' => '创建时间',
-            'UPDATED_AT' => '更新时间',
-            'STATUS' => '状态',
-            'DEC_CLOSED' => '是否关闭报单功能',
-            'DEC_CLOSED_AT' => '关闭时间',
-            'DEC_LV' => '报单级别',
-            'EMP_LV' => '聘级',
-            'CROWN_LV' => '星级',
-            'PROVINCE' => '省份',
-            'CITY' => '城市',
-            'COUNTY' => '县区',
-            'TEL' => '座机',
-            'SUB_COM_ID' => '子公司ID',
-            'AVATAR' => '头像',
-            'DELETED' => '是否删除',
-            'DELETED_AT' => '删除时间',
-            'IS_DEC' => '是否为报单中心',
-            'IS_ATLAS' => '是否显示图谱',
-            'IS_RECHARGE' => '是否显示充值',
-            'DEC_ID' => '报单中心ID',
-            'BIRTHDAY' => '生日',
-            'DEC_ROLE_ID' => '报单中心级别',
-            'PERIOD_AT' => '期数',
-            'DEC_PROVINCE' => '报单中心所属的省份',
-            'DEC_CITY' => '报单中心所属的城市',
-            'DEC_COUNTY' => '报单中心所属的县区',
-            'IS_UNION' => '是否为点位合作',
-            'STATUS_AT' => '状态更改时间',
-            'VERIFIED' => '是否认证',
-            'VERIFIED_AT' => '认证时间',
-            'ALLOW_LOGIN' => '允许登录',
-            'NOT_OPERATING' => '不运作',
-            'REG_FROM' => '注册类型',
-            'ID_CARD_PREFIX' => '身份证前缀',
-            'SEX' => '性别',
-            'DEC_ACCOUNT_OLD' => '老系统中的报单中心编号',
-            'BANK_UPDATED_AT' => '银行信息更新时间',
-            'IS_DIRECT_SELLER' => '是否为直销员',
-            'DEC_LV_UPDATED_AT' => '报单级别更新时间',
-            'DEC_LV_UPDATED_PERIOD' => '报单级别更新期数',
-            'DEC_ADDRESS' => '报单中心详细地址',
-            'DEC_PHONE' => '报单中心电话',
-            'GUARANTOR' => '担保人编号',
-            'GUARANTOR_NAME' => '担保人姓名',
-            'PART_FUNC_CLOSED' => '部分功能开启',
-            'LAST_DEC_LV_UPDATED_PERIOD' => '上次报单级别更新期数',
-            'USER_CREATOR' => '创建人',
-            'USER_UPDATER' => '修改人',
-            'LAST_DEC_LV_UPDATED_AT' => '上次报单级别更新时间',
-            'DEC_CREATED_AT' => '成为报单中心时间',
-            'PART_FUNC_CLOSED_REMARK' => '部分功能关闭原因',
-            'DEC_CREATED_PERIOD' => '成为报单中心期数',
-            'PASSWORD_CHANGED' => '是否修改过密码',
-            'SUB_COM_LEADER' => '是否为分公司领导',
-            'ZG_UPGRADE_PV' => '增购升级PV',
-            'APP_CLIENT_ID' => 'APP设备ID',
-            'READ_AGREEMENT' => '已读协议',
-            'LAST_DEC_LV' => '上次的报单级别',
-            'BONUS_APP_CLIENT_ID' => '结算APP设备ID',
-            'IS_FIRST_OPEN' => '首次开通',
-            'IS_MODIFY_PASSWORD' => '是否修改密码',
-            'IS_STUDIO' => '是否是工作室',
-            'EMAIL' => 'Email',
+            'USER_NAME' => Yii::t('app', 'userName'),
+            'PASSWORD_HASH' => Yii::t('app', 'passwordHash'),
+            'PAY_PASSWORD' => Yii::t('app', 'payPassword'),
+            'NATION' => Yii::t('app', 'nation'),
+            'REAL_NAME' => Yii::t('app', 'realName'),
+            'ID_CARD' => Yii::t('app', 'IDCard'),
+            'ID_TYPE' => Yii::t('app', 'IDType'),
+            'MOBILE' => Yii::t('app', 'mobile'),
+            'ADDRESS' => Yii::t('app', 'address'),
+            'ID_IMAGE' => Yii::t('app', 'IDImage'),
+            'OPEN_BANK' => Yii::t('app', 'openBank'),
+            'BANK_ADDRESS' => Yii::t('app', 'bankAddress'),
+            'BANK_NO' => Yii::t('app', 'bankNo'),
+            'BANK_PROVINCE' => Yii::t('app', 'bankProvince'),
+            'BANK_CITY' => Yii::t('app', 'bankCity'),
+            'BANK_COUNTY' => Yii::t('app', 'bankCounty'),
+            'SPOUSE_NAME' => Yii::t('app', 'spouseName'),
+            'SPOUSE_IDCARD' => Yii::t('app', 'spouseIDCard'),
+            'CREATED_AT' => Yii::t('app', 'createAt'),
+            'UPDATED_AT' => Yii::t('app', 'updateAt'),
+            'STATUS' => Yii::t('app', 'status'),
+            'DEC_CLOSED' => Yii::t('app', 'decClosed'),
+            'DEC_CLOSED_AT' => Yii::t('app', 'decClosedAt'),
+            'DEC_LV' => Yii::t('app', 'decLv'),
+            'EMP_LV' => Yii::t('app', 'empLv'),
+            'CROWN_LV' => Yii::t('app', 'crownLv'),
+            'PROVINCE' => Yii::t('app', 'province'),
+            'CITY' => Yii::t('app', 'city'),
+            'COUNTY' => Yii::t('app', 'county'),
+            'TEL' => Yii::t('app', 'tel'),
+            'SUB_COM_ID' => Yii::t('app', 'subComID'),
+            'AVATAR' => Yii::t('app', 'avatar'),
+            'DELETED' => Yii::t('app', 'deleted'),
+            'DELETED_AT' => Yii::t('app', 'deletedAt'),
+            'IS_DEC' => Yii::t('app', 'isDec'),
+            'IS_ATLAS' => Yii::t('app', 'isAtlas'),
+            'IS_RECHARGE' => Yii::t('app', 'isRecharge'),
+            'DEC_ID' => Yii::t('app', 'DEC_ID'),
+            'BIRTHDAY' => Yii::t('app', 'birthday'),
+            'DEC_ROLE_ID' => Yii::t('app', 'decRoleID'),
+            'PERIOD_AT' => Yii::t('app', 'periodAt'),
+            'DEC_PROVINCE' => Yii::t('app', 'decProvince'),
+            'DEC_CITY' => Yii::t('app', 'decCity'),
+            'DEC_COUNTY' => Yii::t('app', 'decCounty'),
+            'IS_UNION' => Yii::t('app', 'isUnion'),
+            'STATUS_AT' => Yii::t('app', 'statusAt'),
+            'VERIFIED' => Yii::t('app', 'verified'),
+            'VERIFIED_AT' => Yii::t('app', 'verifiedAt'),
+            'ALLOW_LOGIN' => Yii::t('app', 'allowLogin'),
+            'NOT_OPERATING' => Yii::t('app', 'notOperating'),
+            'REG_FROM' => Yii::t('app', 'regFrom'),
+            'ID_CARD_PREFIX' => Yii::t('app', 'IDCardPrefix'),
+            'SEX' => Yii::t('app', 'sex'),
+            'DEC_ACCOUNT_OLD' => Yii::t('app', 'decAccountOld'),
+            'BANK_UPDATED_AT' => Yii::t('app', 'bankUpdatedAt'),
+            'IS_DIRECT_SELLER' => Yii::t('app', 'isDirectSeller'),
+            'DEC_LV_UPDATED_AT' => Yii::t('app', 'decLvUpdatedAt'),
+            'DEC_LV_UPDATED_PERIOD' => Yii::t('app', 'decLvUpdatedPeriod'),
+            'DEC_ADDRESS' => Yii::t('app', 'decAddress'),
+            'DEC_PHONE' => Yii::t('app', 'decPhone'),
+            'GUARANTOR' => Yii::t('app', 'guarantor'),
+            'GUARANTOR_NAME' => Yii::t('app', 'guarantorName'),
+            'PART_FUNC_CLOSED' => Yii::t('app', 'partFuncClosed'),
+            'LAST_DEC_LV_UPDATED_PERIOD' => Yii::t('app', 'lastDecLvUpdatedPeriod'),
+            'USER_CREATOR' => Yii::t('app', 'userCreator'),
+            'USER_UPDATER' => Yii::t('app', 'userUpdater'),
+            'LAST_DEC_LV_UPDATED_AT' => Yii::t('app', 'lastDecLvUpdatedAt'),
+            'DEC_CREATED_AT' => Yii::t('app', 'decCreatedAt'),
+            'PART_FUNC_CLOSED_REMARK' => Yii::t('app', 'partFuncClosedRemark'),
+            'DEC_CREATED_PERIOD' => Yii::t('app', 'decCreatedPeriod'),
+            'PASSWORD_CHANGED' => Yii::t('app', 'passwordChanged'),
+            'SUB_COM_LEADER' => Yii::t('app', 'subComLeader'),
+            'ZG_UPGRADE_PV' => Yii::t('app', 'zgUpgradePv'),
+            'APP_CLIENT_ID' => Yii::t('app', 'appClientID'),
+            'READ_AGREEMENT' => Yii::t('app', 'readAgreement'),
+            'LAST_DEC_LV' => Yii::t('app', 'lastDecLv'),
+            'BONUS_APP_CLIENT_ID' => Yii::t('app', 'bonusAppClientID'),
+            'IS_FIRST_OPEN' => Yii::t('app', 'isFirstOpen'),
+            'IS_MODIFY_PASSWORD' => Yii::t('app', 'isModifyPassword'),
+            'IS_STUDIO' => Yii::t('app', 'isStudio'),
+            'EMAIL' => Yii::t('app', 'email'),
         ];
     }
 
@@ -360,15 +360,15 @@ class User extends \common\components\ActiveRecord
             'DEC_ROLE_ID' => $userInfo['DEC_ROLE_ID'],
             'OPEN_BANK' => $userInfo['OPEN_BANK'],
             'BANK_ADDRESS' => $userInfo['BANK_ADDRESS'],
-            'DEC_LV' => $userInfo['DEC_LV'],
-            'EMP_LV' => $userInfo['EMP_LV'],
-            'CROWN_LV' => $userInfo['CROWN_LV'],
+            'DEC_LV' => $userInfo['DEC_LV'] ?? DeclarationLevel::getDefaultLevelId(),
+            'EMP_LV' => $userInfo['EMP_LV'] ?? EmployLevel::getDefaultLevelId(),
+            'CROWN_LV' => $userInfo['CROWN_LV'] ?? StarCrownLevel::getDefaultLevelId(),
             'PROVINCE' => $userInfo['PROVINCE'],
             'CITY' => $userInfo['CITY'],
             'COUNTY' => $userInfo['COUNTY'],
             'BANK_NO' => $userInfo['BANK_NO'],
 //            'BANK_NO' => Tool::hideBankNo($userInfo['BANK_NO']),
-            'EMAIL' => $userInfo['EMAIL'],
+            'EMAIL' => $userInfo['EMAIL'] ?? '',
         ];
     }
 

+ 0 - 1
common/models/forms/AdminAddUserForm.php

@@ -224,7 +224,6 @@ class AdminAddUserForm extends Model {
             $user->REAL_NAME = $this->realName;
 //            $user->ID_CARD = $this->idCard;
             $user->MOBILE = $this->mobile;
-//            $user->ADDRESS = 'nothing';//无
             $user->ADDRESS = $this->address ? $this->address : 'nothing';//无
             $user->OPEN_BANK = $this->openBank;
             $user->BANK_ADDRESS = $this->bankAddress;

+ 17 - 15
common/models/forms/ApproachDeclarationForm.php

@@ -28,6 +28,7 @@ use common\models\User;
 use common\models\UserInfo;
 use common\models\UserNetwork;
 use common\models\UserRelation;
+use Yii;
 use yii\base\Exception;
 
 /**
@@ -195,7 +196,7 @@ class ApproachDeclarationForm extends Model
      */
     public function issetDec($attribute){
         if (!$decUser = User::find()->select('ID')->where('IS_DEC=1 AND USER_NAME=:USER_NAME', [':USER_NAME' => $this->decUserName])->asArray()->one()) {
-            $this->addError($attribute, 'Stockist does not exist');//报单中心不存在
+            $this->addError($attribute, Yii::t('app', 'stockistDoesNotExist'));
             return false;
         } else {
             if ($this->decType !== 'ba') {
@@ -376,7 +377,7 @@ class ApproachDeclarationForm extends Model
         else {
             if($this->insertUserName == $conUserName){
                // $this->addError('recUserName', '新加入的会员不存在');
-                $this->addError('recUserName', 'New member does not exist');
+                $this->addError('recUserName', Yii::t('app', 'newMemberDoesNotExist'));
                 return false;
             }
             // 去数据库里查找这个会员的所有上级
@@ -418,7 +419,7 @@ class ApproachDeclarationForm extends Model
         if($this->type == self::TYPE_ZC){
             if(!in_array($this->location, [1, 2, 3])){
                 //$this->addError($attribute, '市场必须在第1市场、第2市场或第三3市场');
-                $this->addError($attribute, 'Please select a market');
+                $this->addError($attribute, Yii::t('app', 'pleaseSelectMarket'));
             }
             $conUser = $recUser = null;
             if(isset($this->_tempParentUser[$this->conUserName])){
@@ -556,16 +557,16 @@ class ApproachDeclarationForm extends Model
         if(array_key_exists($this->type, $this->_types)){
             if($this->type == self::TYPE_ZC){
                 if($this->scenario == 'userDec'){
-                    if(!$this->insertUserName) $this->addError($attribute, 'Membership number must be filled in for initial purchase');//首购必须填写加入会员编号
+                    if(!$this->insertUserName) $this->addError($attribute, Yii::t('app', 'MembershipNumberFilledInitialPurchase'));
                 }
-                if(!$this->insertUserName) $this->addError($attribute, 'Membership number must be filled in for initial purchase');//首购必须填写加入会员编号
+                if(!$this->insertUserName) $this->addError($attribute, Yii::t('app', 'MembershipNumberFilledInitialPurchase'));
 //                if(!$this->insertUserIdCard) $this->addError($attribute, 'For the first purchase, the ID number of the member must be filled in');//首购必须填写加入会员的身份证号
-                if(!$this->conUserName) $this->addError($attribute, 'For the first purchase, you must fill in the instructor number of the member');//首购必须填写加入会员的指导老师编号
-                if(!$this->recUserName) $this->addError($attribute, 'For the first purchase, the Sponsor number of the member must be filled in');//首购必须填写加入会员的开拓人编号
-                if(!$this->location) $this->addError($attribute, 'The first purchase must be filled in the market of the member');//首购必须填写加入会员的市场
+                if(!$this->conUserName) $this->addError($attribute, Yii::t('app', 'fillTheInstructorNumberTheMember'));
+                if(!$this->recUserName) $this->addError($attribute, Yii::t('app', 'sponsorNumberMustBeFilled'));
+                if(!$this->location) $this->addError($attribute, Yii::t('app', 'beFilledTheMarketMember'));
             }
         } else {
-            $this->addError($attribute, 'Incorrect entry type');//报单类型不正确
+            $this->addError($attribute, Yii::t('app', 'incorrectEntryType'));
         }
     }
 
@@ -617,14 +618,14 @@ class ApproachDeclarationForm extends Model
         if($this->type == self::TYPE_ZC) {
             try {
                 if (preg_match("/[\x7f-\xff]/", $this->insertUserName)) {  //判断字符串中是否有中文
-                    throw new Exception('Member number cannot contain Chinese characters');//会员编号中不能含有汉字
+                    throw new Exception(Yii::t('app', 'memberNumberCanNotContainChineseCharacters'));
                 }
                 //报单商品及PV判断
                 $decLevelConfig = Cache::getDecLevelConfig();
                 $decLevel = $decLevelConfig[$this->decLv];
                 $toDecLevel = $this->decLv;
                 if (!$this->decLv) {
-                    throw new Exception('Please select the entry level');//请选择报单级别
+                    throw new Exception(Yii::t('app', 'pleaseSelectTheEntryLevel'));
                 }
 
                 if ($this->decWay == 1) {
@@ -676,7 +677,7 @@ class ApproachDeclarationForm extends Model
                         }
                     }
                     if ($totalPv < $decLevel['PERF']) {
-                        throw new Exception('The total BV of self selected goods cannot be less than the BV of the selected entry level');//自选商品总BV不能小于所选报单级别BV
+                        throw new Exception(Yii::t('app', 'totalBVCanNotLessThanSelectedBV'));
                     }
                     foreach ($decLevelConfig as $key => $val) {
                         if ($totalPv >= $val['PERF']) {
@@ -684,7 +685,7 @@ class ApproachDeclarationForm extends Model
                         }
                     }
                     if ($this->decLv != $toDecLevel) {
-                        throw new Exception('The total BV of self selected goods cannot exceed the BV value of the next level under the selected level');//自选商品总BV不能超过已选级别下一个级别的BV值
+                        throw new Exception(Yii::t('app', 'totalBVCanNotLessThanNextSelectedLevelBV'));
                     }
                     $this->_decAmount = $totalAmount;
                     $this->_decPv = $totalPv;
@@ -703,7 +704,8 @@ class ApproachDeclarationForm extends Model
 
                 return $decResult;
             } catch (\Exception $e) {
-                throw new Exception(Form::formatErrorsForApi($e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage()));
+//                throw new Exception(Form::formatErrorsForApi($e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage()));
+                throw new Exception(Form::formatErrorsForApi($e->getMessage()));
             }
         }
         return true;
@@ -819,7 +821,7 @@ class ApproachDeclarationForm extends Model
             $warehouse = Region::getWarehouseByCode($this->province);
             if (!$warehouse) {
                 // 地区暂时不支持配送,具体联系客服
-                throw new Exception('Delivery is temporarily not supported in the region. Contact customer service for details');
+                throw new Exception(Yii::t('app', 'deliveryTemporarilyNotSupported'));
             }
         } else {
             $warehouse = '01';

+ 11 - 9
common/models/forms/ApproachDeclarationLoopForm.php

@@ -4,6 +4,7 @@ namespace common\models\forms;
 use common\components\Model;
 use common\helpers\Form;
 use common\models\UserBind;
+use Yii;
 use yii\base\Exception;
 use yii\helpers\Json;
 use common\models\DeclarationPackage;
@@ -90,7 +91,7 @@ class ApproachDeclarationLoopForm extends Model
                 $model->location = null;
                 $model->decType = null;
             } else {
-                $this->addError($attribute, 'The format of the report data is incorrect');// 报单数据格式错误
+                $this->addError($attribute, Yii::t('app', 'reportFormatIncorrect'));
             }
 
         }
@@ -121,7 +122,7 @@ class ApproachDeclarationLoopForm extends Model
                 if (isset($value['packageId']) && $value['packageId']){
                     $packagedata = DeclarationPackage::findOneAsArray('ID=:ID', [':ID' => $value['packageId']]);
                     if (!$packagedata) {
-                        throw new Exception('Products does not exists!');
+                        throw new Exception(Yii::t('app', 'productsDoesNotExists'));
                     }
                     if($packagedata['STORE_NUMS']>0){
                         $data =  DeclarationPackage::find()->where(['ID'=> $packagedata['ID'] ])->one();
@@ -135,7 +136,7 @@ class ApproachDeclarationLoopForm extends Model
                             $data->UPDATED_AT = Date::nowTime();
                         }
                     }else{
-                        throw new Exception($packagedata['PACKAGE_NAME'].'Insufficient inventory');// 库存不足
+                        throw new Exception($packagedata['PACKAGE_NAME'] . Yii::t('app', 'insufficientInventory'));
                     }
                 }
                 // 普通商品报单
@@ -143,7 +144,7 @@ class ApproachDeclarationLoopForm extends Model
                     for ($i = 0; $i < count($value['goodsId']) ;$i++) {
                         $goods = ShopGoods::findOneAsArray('ID=:ID',[':ID'=> $value['goodsId'][$i]]);
                         if (!$goods) {
-                            throw new Exception('Products does not exists!');
+                            throw new Exception(Yii::t('app', 'productsDoesNotExists'));
                         }
                         if ($goods['STATUS'] == 1 ){
                             if($goods['STORE_NUMS'] >= $value['goodsNum'][$i]) {
@@ -159,10 +160,10 @@ class ApproachDeclarationLoopForm extends Model
                                     $data->update();
                                 }
                             } else {
-                                throw new Exception($goods['GOODS_NAME'].'Insufficient inventory');// 商品库存不足
+                                throw new Exception($goods['GOODS_NAME'] . Yii::t('app', 'insufficientInventory'));
                             }
                         }else{
-                            throw new Exception($goods['GOODS_NAME'].'Sold out');// 商品已下架
+                            throw new Exception($goods['GOODS_NAME'] . Yii::t('app', 'soldOut'));
                         }
 
                     }
@@ -177,7 +178,7 @@ class ApproachDeclarationLoopForm extends Model
                     if( $model->type == 'ZC'){
                         if($zcUserIdCard != null){
                             if($model->insertUserIdCard != $zcUserIdCard){
-                                throw new Exception('Bulk declaration member must be the same member');//批量报单会员必须是同一身份证
+                                throw new Exception(Yii::t('app', 'bulkDeclarationNotSames'));
                             }
                         } else {
                             $zcUserIdCard = $model->insertUserIdCard;
@@ -189,7 +190,7 @@ class ApproachDeclarationLoopForm extends Model
                         throw new Exception(Form::formatErrorsForApi($model->getErrors()));
                     }
                 } else {
-                    throw new Exception('The format of the report data is incorrect');// 报单数据格式错误
+                    throw new Exception(Yii::t('app', 'reportFormatIncorrect'));
                 }
             }
 
@@ -198,7 +199,8 @@ class ApproachDeclarationLoopForm extends Model
             return $result;
         } catch (\Exception $e){
             $transaction->rollBack();
-            $this->addError('add', $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage());
+//            $this->addError('add', $e->getFile() . ' ' . $e->getLine() . ' ' . $e->getMessage());
+            $this->addError('add', $e->getMessage());
             return null;
         }
     }

+ 8 - 7
common/models/forms/ApproachDeclarationUpgradeForm.php

@@ -25,6 +25,7 @@ use common\models\ShopGoods;
 use common\models\User;
 use common\models\UserInfo;
 use common\models\UserNetwork;
+use Yii;
 use yii\base\Exception;
 
 /**
@@ -119,13 +120,13 @@ class ApproachDeclarationUpgradeForm extends Model
             $decLevel = $decLevelConfig[$this->decLv];
             $toDecLevel = $this->decLv;
             if(!$this->decLv){
-                throw new Exception('Please select upgrade level');//请选择升级级别
+                throw new Exception(Yii::t('app', 'pleaseSelectUpgradeLevel'), 400);
             }
             $baseInfo = Info::baseInfoZhByUserName($this->insertUserName);
             $userId = $baseInfo['ID'];
             $userDecPvSum = User::sumDevPvByUserId($userId); // 用户所有报单PV总和
             if ($userDecPvSum != $this->nowPerf) {
-                throw new Exception('Please contact the customer service personnel to check the performance of upgraded members');//请联系客服人员核对升级会员业绩
+                throw new Exception(Yii::t('app', 'checkPerformanceOfUpgradedMember'), 400);
             }
             // 获取用户是否是观察期
             $observe = Config::getConfigByType('observe'); // 获取观察期配置信息
@@ -133,7 +134,7 @@ class ApproachDeclarationUpgradeForm extends Model
             $isObserve = User::checkIsObserve($baseInfo['CREATED_AT'], $observeLimit); // 判断用户是否再观察期中
             $diffPerf = $isObserve ? $this->nowPerf : 0; // 观察期内升级要加上用户累计的PV,全额则基础PV为0,全额购买
             if ($this->decWay != 2) {
-                throw new Exception('The upgrade method is incorrect. Please contact the customer service personnel');//升级方式不正确,请联系客服人员
+                throw new Exception(Yii::t('app', 'upgradeMethodIncorrect'), 400);
             }
             if($this->decWay==1) {
                 // 先不加套餐升级方式
@@ -189,7 +190,7 @@ class ApproachDeclarationUpgradeForm extends Model
                 // 这里特殊是用户原报单PV之和+用户购买的商品总PV
                 $checkPv = $totalPv + $diffPerf;
                 if($checkPv < $decLevel['PERF']) {
-                    throw new Exception('Total PV cannot be less than the selected level PV');//总PV不能小于所选级别PV
+                    throw new Exception(Yii::t('app', 'totalPVLessThan'), 400);
                 }
                 foreach ($decLevelConfig as $key=>$val){
                     if($checkPv>=$val['PERF']){
@@ -197,7 +198,7 @@ class ApproachDeclarationUpgradeForm extends Model
                     }
                 }
                 if($this->decLv!=$toDecLevel){
-                    throw new Exception('The total PV cannot exceed the PV value of the next level under the selected level');//总PV不能超过已选级别下一个级别的PV值
+                    throw new Exception(Yii::t('app', 'totalPvExceedPv'), 400);
                 }
                 $this->_decAmount = $totalAmount;
                 $this->_decPv = $totalPv;
@@ -211,7 +212,7 @@ class ApproachDeclarationUpgradeForm extends Model
             $insertRecId = $baseInfo['REC_UID'];
             $decResult = $this->addDecOrder($insertConId, $insertRecId, $baseInfo['DEC_LV'], $isObserve, $this->remark);
             if (!$decResult) {
-                throw new Exception("operation failed");//操作失败
+                throw new Exception(Yii::t('app', 'failed'), 400);
             }
             return $decResult;
         }
@@ -226,7 +227,7 @@ class ApproachDeclarationUpgradeForm extends Model
     {
         $warehouse = Region::getWarehouseByCode($this->province);//仓库
         if(!$warehouse){
-            throw new Exception('Delivery is temporarily not supported in the region. Contact customer service for details');//地区暂时不支持配送,具体联系客服
+            throw new Exception(Yii::t('app', 'deliveryTemporarilyNotSupported'), 400);
         }
 
         $upgradeType = $isObserve ? 1 : 2; // 1补差  2全额

+ 47 - 48
common/models/forms/ApproachOrderForm.php

@@ -26,6 +26,7 @@ use common\models\Region;
 use common\models\ShopGoods;
 use common\models\User;
 use common\models\UserNetwork;
+use Yii;
 use yii\base\Exception;
 
 /**
@@ -97,28 +98,26 @@ class ApproachOrderForm extends Model
     public function attributeLabels()
     {
         return [
-            'sn' => 'Order code',//订单号
-            'expressCompany' => 'ExpressCompany',//快递公司
-            'orderTrackNo' => 'courier number',//快递单号
-            'status' => 'status',//状态
-            'remark' => 'remark',//备注
-            'type' => 'order type',//订单类型
-            'addressId' => 'addressId',//收货地址
-            'payType' => 'payType',//支付方式
-            'goodsId' => 'Product ID',
-            'goodsNum' => 'Product quantity',//
-            'userName' => '复消会员编号',//复消会员编号
-            'consignee' => '收货人',//收货人
-            'acceptMobile' => '收货电话',//收货电话
-            'province' => '省',
-            'city' => '市',
-            'county' => '区',
-
-            'lgaName' => 'lga name',
-            'cityName' => 'City Name',
-            'detailaddress' => '收货详细地址',
-
-            'email' => 'Email',
+            'sn' => Yii::t('app', 'orderSn'),
+            'expressCompany' => Yii::t('app', 'expressCompany'),
+            'orderTrackNo' => Yii::t('app', 'orderTrackNo'),
+            'status' => Yii::t('app', 'state'),
+            'remark' => Yii::t('app', 'remark'),
+            'type' => Yii::t('app', 'orderType'),
+            'addressId' => Yii::t('app', 'shippingAddress'),
+            'payType' => Yii::t('app', 'payType'),
+            'goodsId' => Yii::t('app', 'productID'),
+            'goodsNum' => Yii::t('app', 'quantity'),
+            'userName' => Yii::t('app', 'repeatSalesMemberNo'),
+            'consignee' => Yii::t('app', 'consignee'),
+            'acceptMobile' => Yii::t('app', 'acceptMobile'),
+            'province' => Yii::t('app', 'province'),
+            'city' => Yii::t('app', 'city'),
+            'county' => Yii::t('app', 'county'),
+            'lgaName' =>Yii::t('app', 'lgaName'),
+            'cityName' => Yii::t('app', 'cityName'),
+            'detailaddress' => Yii::t('app', 'detailAddress'),
+            'email' => Yii::t('app', 'email'),
         ];
     }
 
@@ -150,14 +149,14 @@ class ApproachOrderForm extends Model
         if ($this->sn) {
             $this->_model = ApproachOrder::findOne(['SN' => $this->sn]);
             if (!$this->_model){
-                $this->addError('sn', '订单不存在');
+                $this->addError('sn', Yii::t('app', 'orderDoesNotExist'));
                 return false;
             }
         }
 
         if ($this->scenario == 'verifyPayStack'){
             if ($this->_model->STATUS != \Yii::$app->params['orderStatus']['notPaid']['value']) {
-                $this->addError('sn', '订单支付状态错误');
+                $this->addError('sn', Yii::t('app', 'payTypeError'));
                 return false;
             }
         }
@@ -171,7 +170,7 @@ class ApproachOrderForm extends Model
      */
     public function isAddress($attribute){
         if (!$receiveAddress = ReceiveAddress::find()->where(' ID=:ID', [':ID' => $this->addressId])->asArray()->one()) {
-            $this->addError($attribute, '收货地址不存在');
+            $this->addError($attribute, Yii::t('app', 'shippingDoesNotExist'));
         } else {
             $this->_address = $receiveAddress;
         }
@@ -184,7 +183,7 @@ class ApproachOrderForm extends Model
      */
     public function validatePassword($attribute, $params) {
         if (!User::validatePayPassword(\Yii::$app->user->id, $this->payPassword)) {
-            $this->addError($attribute, 'The payment password is incorrect');//支付密码不正确
+            $this->addError($attribute, Yii::t('app', 'paymentPasswordError'));
         }
     }
 
@@ -195,26 +194,26 @@ class ApproachOrderForm extends Model
     public function isPayType($attribute)
     {
         if ($this->payType != 'pay_stack'){
-            $this->addError($attribute, '只允许PayStack方式支付');
+            $this->addError($attribute, Yii::t('app', 'payTypeError'));
             return;
         }
 
         // 一个订单只能包含一类商品
         $goods = ShopGoods::find()->select('ID,CATEGORY_TYPE')->where(['in', 'ID', $this->goodsId])->andWhere(['STATUS' => 1])->asArray()->all();
         if (!$goods) {
-            $this->addError($attribute, 'Products does not exists!');
+            throw new Exception(Yii::t('app', 'orderCanNotContainMultipleProductCategories'));
             return;
         }
         $goodsCategoryType = array_unique(array_column($goods, 'CATEGORY_TYPE'));
         if (count($goodsCategoryType) > 1) {
-            $this->addError($attribute, 'Order cannot contain multiple product categories');//订单不能包含多种商品分类
+            $this->addError($attribute, Yii::t('app', 'orderCanNotHasMoreClassification'));
             return;
         }
 
         // 购买方式
         $sellTypeLabelMap = array_column(ShopGoods::SALE_TYPE, NULL, 'label');
         if (!array_key_exists($this->payType, $sellTypeLabelMap)) {
-            $this->addError($attribute, '不支持PayStack方式支付');
+            $this->addError($attribute, Yii::t('app', 'payTypeError'));
             return;
         }
 
@@ -223,13 +222,13 @@ class ApproachOrderForm extends Model
         // 商品类型
         $currCategoryType = $goodsCategoryType[0];
         if (!array_key_exists($currCategoryType, $categoryType)) {
-            $this->addError($attribute, '商品分类错误');
+            $this->addError($attribute, Yii::t('app', 'shopGoodClassificationError'));
             return;
         }
 
         $sellType = $categoryType[$currCategoryType]['sell_type'] ?? [];
         if (!$sellType || !in_array($this->payType, array_column($sellType, 'label'))) {
-            $this->addError($attribute, '支付方式错误3');
+            $this->addError($attribute, Yii::t('app', 'payTypeError'));
         }
     }
 
@@ -238,44 +237,44 @@ class ApproachOrderForm extends Model
      * @param $attribute
      */
     public function isStatus($attribute){
-        if($this->type && !in_array($this->type, \Yii::$app->params['orderStatus'])){
-            $this->addError($attribute, '订单状态类型错误');
+        if(!in_array($this->type, \Yii::$app->params['orderStatus'])){
+            $this->addError($attribute, Yii::t('app', 'orderStatusTypeError'));
             return ;
         }
         if ($this->scenario == 'adminStatus'){
             if ($this->status == $this->_model['STATUS']) {
-                $this->addError($attribute, '订单状态没有改变');
+                $this->addError($attribute, Yii::t('app', 'orderStatusDoesNotChange'));
                 return ;
             }
             if($this->status == \Yii::$app->params['orderStatus']['notPaid'] && $this->_model['STATUS'] >= \Yii::$app->params['orderStatus']['delivery']) {
-                $this->addError($attribute, '订单已经进入物流状态不能改为未支付');
+                $this->addError($attribute, Yii::t('app', 'orderHasBeenLogisticsStatusDoesNotChangedUnpaid'));
                 return ;
             }
             elseif($this->status == \Yii::$app->params['orderStatus']['paid'] && $this->_model['STATUS'] >= \Yii::$app->params['orderStatus']['cancel']) {
-                $this->addError($attribute, '订单已失效不能处理');
+                $this->addError($attribute, Yii::t('app', 'orderHasBeenInvalidCanNotProcess'));
                 return ;
             }
             elseif($this->status == \Yii::$app->params['orderStatus']['delivery']) {
-                $this->addError($attribute, '订单不能单独处理为物流状态');
+                $this->addError($attribute, Yii::t('app', 'orderCanNotBeenChangedLogistics'));
                 return ;
             }
             elseif($this->status == \Yii::$app->params['orderStatus']['complete'] && $this->_model['STATUS'] > \Yii::$app->params['orderStatus']['cancel']) {
-                $this->addError($attribute, '订单已失效不能处理');
+                $this->addError($attribute, Yii::t('app', 'orderHasBeenInvalidCanNotProcess'));
                 return ;
             }
             elseif($this->status == \Yii::$app->params['orderStatus']['cancel']) {
                 if($this->_model['STATUS'] == \Yii::$app->params['orderStatus']['complete']) {
-                    $this->addError($attribute, '订单已完成不能取消');
+                    $this->addError($attribute, Yii::t('app', 'orderHasBeenFinishedCanNotCancel'));
                     return ;
                 }
                 if($this->_model['STATUS'] == \Yii::$app->params['orderStatus']['del']) {
-                    $this->addError($attribute, '订单已删除不能取消');
+                    $this->addError($attribute, Yii::t('app', 'orderHasBeenDeletedCanNotCancel'));
                     return ;
                 }
             }
             elseif($this->status == \Yii::$app->params['orderStatus']['del']) {
                 if($this->_model['STATUS'] == \Yii::$app->params['orderStatus']['complete']) {
-                    $this->addError($attribute, '订单已完成不能删除');
+                    $this->addError($attribute, Yii::t('app', 'orderHasBeenFinishedCanNotDelete'));
                     return ;
                 }
             }
@@ -301,7 +300,7 @@ class ApproachOrderForm extends Model
             throw new Exception(Form::formatErrorsForApi($payload['message']));
         }
         if ($payload['data']['amount'] != $this->_model->PAY_AMOUNT * 100) {
-            throw new Exception(Form::formatErrorsForApi('支付金额与订单金额不符'));
+            throw new Exception(Form::formatErrorsForApi(Yii::t('app', 'payAmountNotEqualOrderAmount')));
         }
 
         // 订单类型:userOrder(会员订单)、userUpgrade(会员升级)、userDec(会员报单)
@@ -343,7 +342,7 @@ class ApproachOrderForm extends Model
                     // 修改会员锁定状态
                     if (in_array($orderType, ['userDec', 'baUpgrade'])) {
                         if (!User::updateAll(['STATUS' => 1], 'ID=:USER_ID', [':USER_ID' => $approachDecOrder['TO_USER_ID']])) {
-                            throw new Exception(Form::formatErrorsForApi('change user status error'));
+                            throw new Exception(Form::formatErrorsForApi(Yii::t('app', 'changeUserStatusError')));
                         }
                     }
                     // 修改BA会员升级状态
@@ -351,7 +350,7 @@ class ApproachOrderForm extends Model
                         // 查询BA会员名
                         $userInfo = User::findOneAsArray('ID=:USER_ID', [':USER_ID' => $approachDecOrder['TO_USER_ID']]);
                         if (!BaUser::updateAll(['WHETHER_UPGRADE' => 1, 'BA_UPGRADE_AT' => time()], 'USER_NAME=:USER_NAME', [':USER_NAME' => $userInfo['USER_NAME']])) {
-                            throw new Exception(Form::formatErrorsForApi('Brand Ambassador upgrade error!'));
+                            throw new Exception(Form::formatErrorsForApi(Yii::t('app', 'brandAmbassadorUpgradeError')));
                         }
                     }
 
@@ -372,7 +371,7 @@ class ApproachOrderForm extends Model
                             $modifyDecLv = $decLevelLog->frontendChange($decLog);
                             if (empty($modifyDecLv)) {
                                 $transaction->rollBack();
-                                throw new Exception("Failed to upgrade for member");//为会员升级失败
+                                throw new Exception(Yii::t('app', 'failedToUpgrade'));
                             }
                         }
                     }
@@ -416,7 +415,7 @@ class ApproachOrderForm extends Model
             if ($v) {
                 $goods = ShopGoods::findOneAsArray('ID=:ID AND STATUS=1',[':ID'=> $ids[$k]]);
                 if (!$goods) {
-                    throw new Exception('Products does not exists!');
+                    throw new Exception(Yii::t('app', 'productsDoesNotExists'));
                 }
                 if($goods['STORE_NUMS']>0){
                     if ($goods['TYPE'] == 1 || $goods['TYPE'] == 2) {
@@ -512,7 +511,7 @@ class ApproachOrderForm extends Model
         if ($this->_address['PROVINCE'] != 1) {
             $warehouse = Region::getWarehouseByCode($this->_address['PROVINCE']);//仓库
             if (!$warehouse) {
-//                throw new Exception('地区2暂时不支持配送,具体联系客服');
+//                throw new Exception(Yii::t('app', 'deliveryTemporarilyNotSupported'));
             }
         }else{
             $warehouse = '01';

+ 9 - 8
common/models/forms/BaApproachDeclarationLoopForm.php

@@ -4,6 +4,7 @@ namespace common\models\forms;
 use common\components\Model;
 use common\helpers\Form;
 use common\models\UserBind;
+use Yii;
 use yii\base\Exception;
 use yii\helpers\Json;
 use common\models\DeclarationPackage;
@@ -58,7 +59,7 @@ class BaApproachDeclarationLoopForm extends Model
     public function formatData($attribute){
         //$this->data = Json::decode($this->data);
         if(!is_array($this->data)){
-            $this->addError($attribute, 'Data format error');// 数据格式错误
+            $this->addError($attribute, Yii::t('app', 'dataFormatError'));
         }
     }
 
@@ -89,7 +90,7 @@ class BaApproachDeclarationLoopForm extends Model
                 $model->recUserName = null;
                 $model->location = null;
             } else {
-                $this->addError($attribute, 'The format of the report data is incorrect');// 报单数据格式错误
+                $this->addError($attribute, Yii::t('app', 'reportFormatIncorrect'));
             }
 
         }
@@ -120,7 +121,7 @@ class BaApproachDeclarationLoopForm extends Model
                 if (isset($value['packageId']) && $value['packageId']){
                     $packagedata = DeclarationPackage::findOneAsArray('ID=:ID', [':ID' => $value['packageId']]);
                     if (!$packagedata) {
-                        throw new Exception('Products does not exists!');
+                        throw new Exception(Yii::t('app', 'productsDoesNotExists'));
                     }
                     if($packagedata['STORE_NUMS']>0){
                         $data =  DeclarationPackage::find()->where(['ID'=> $packagedata['ID'] ])->one();
@@ -134,7 +135,7 @@ class BaApproachDeclarationLoopForm extends Model
                             $data->UPDATED_AT = Date::nowTime();
                         }
                     }else{
-                        throw new Exception($packagedata['PACKAGE_NAME'].'Insufficient inventory');// 库存不足
+                        throw new Exception($packagedata['PACKAGE_NAME']. Yii::t('app', 'insufficientInventory'));
                     }
                 }
                 // 普通商品报单
@@ -158,10 +159,10 @@ class BaApproachDeclarationLoopForm extends Model
                                     $data->update();
                                 }
                             } else {
-                                throw new Exception($goods['GOODS_NAME'].'Insufficient inventory');// 商品库存不足
+                                throw new Exception($goods['GOODS_NAME']. Yii::t('app', 'insufficientInventory'));
                             }
                         }else{
-                            throw new Exception($goods['GOODS_NAME'].'Sold out');// 商品已下架
+                            throw new Exception($goods['GOODS_NAME']. Yii::t('app', 'soldOut'));
                         }
                     }
                 }
@@ -174,7 +175,7 @@ class BaApproachDeclarationLoopForm extends Model
                     if( $model->type == 'ZC'){
                         if($zcUserIdCard != null){
                             if($model->insertUserIdCard != $zcUserIdCard){
-                                throw new Exception('Bulk declaration member must be the same member');//批量报单会员必须是同一身份证
+                                throw new Exception(Yii::t('app', 'bulkDeclarationNotSames'));
                             }
                         } else {
                             $zcUserIdCard = $model->insertUserIdCard;
@@ -186,7 +187,7 @@ class BaApproachDeclarationLoopForm extends Model
                         throw new Exception(Form::formatErrorsForApi($model->getErrors()));
                     }
                 } else {
-                    throw new Exception('The format of the report data is incorrect');// 报单数据格式错误
+                    throw new Exception(Yii::t('app', 'reportFormatIncorrect'));
                 }
             }
 

+ 38 - 38
common/models/forms/BaApproachOrderForm.php

@@ -33,6 +33,7 @@ use common\models\Region;
 use common\models\ShopGoods;
 use common\models\User;
 use common\models\UserNetwork;
+use Yii;
 use yii\base\Exception;
 
 /**
@@ -100,23 +101,23 @@ class BaApproachOrderForm extends Model
     public function attributeLabels(): array
     {
         return [
-            'sn' => 'Order code',//订单号
-            'expressCompany' => 'Express Company',//快递公司
-            'orderTrackNo' => 'Track Code',//快递单号
-            'status' => 'Status',//状态
-            'remark' => 'Remark',//备注
-            'type' => 'Order Type',//订单类型
-            'addressId' => 'AddressId',//收货地址
-            'payType' => 'PayType',//支付方式
-            'goodsId' => 'Product ID',// 商品编号
-            'goodsNum' => 'Quantity',//
-            'userName' => 'UserName',//复消会员编号
-            'consignee' => 'Consignee',//收货人
-            'acceptMobile' => 'Accept Mobile',//收货电话
-            'lgaName' => 'LgaName',
-            'cityName' => 'CityName',
-            'detailaddress' => 'Detail Address',// 收货详细地址
-            'email' => 'Email',
+            'sn' => Yii::t('app', 'orderSn'),
+            'expressCompany' => Yii::t('app', 'expressCompany'),
+            'orderTrackNo' => Yii::t('app', 'orderTrackNo'),
+            'status' => Yii::t('app', 'state'),
+            'remark' => Yii::t('app', 'remark'),
+            'type' => Yii::t('app', 'orderType'),
+            'addressId' => Yii::t('app', 'shippingAddress'),
+            'payType' => Yii::t('app', 'payType'),
+            'goodsId' => Yii::t('app', 'productID'),
+            'goodsNum' => Yii::t('app', 'quantity'),
+            'userName' => Yii::t('app', 'repeatSalesMemberNo'),
+            'consignee' => Yii::t('app', 'consignee'),
+            'acceptMobile' => Yii::t('app', 'acceptMobile'),
+            'lgaName' =>Yii::t('app', 'lgaName'),
+            'cityName' => Yii::t('app', 'cityName'),
+            'detailaddress' => Yii::t('app', 'detailAddress'),
+            'email' => Yii::t('app', 'email'),
         ];
     }
 
@@ -148,14 +149,14 @@ class BaApproachOrderForm extends Model
         if ($this->sn) {
             $this->_model = BaApproachOrder::findOne(['SN' => $this->sn]);
             if (!$this->_model){
-                $this->addError('sn', 'Order does not exist');// 订单不存在
+                $this->addError('sn', Yii::t('app', 'orderDoesNotExist'));
                 return false;
             }
         }
 
         if ($this->scenario == 'verifyPayStack'){
             if ($this->_model->STATUS != \Yii::$app->params['orderStatus']['notPaid']['value']) {
-                $this->addError('sn', '订单支付状态错误');
+                $this->addError('sn', Yii::t('app', 'payTypeError'));
                 return false;
             }
         }
@@ -171,7 +172,7 @@ class BaApproachOrderForm extends Model
     public function isAddress($attribute, $params)
     {
         if (!$receiveAddress = BaReceiveAddress::find()->where(' ID=:ID', [':ID' => $this->addressId])->asArray()->one()) {
-            $this->addError($attribute, 'Receive address does not exist'); // 收货地址不存在
+            $this->addError($attribute, Yii::t('app', 'shippingDoesNotExist'));
         } else {
             $this->_address = $receiveAddress;
         }
@@ -184,7 +185,7 @@ class BaApproachOrderForm extends Model
      */
     public function validatePassword($attribute, $params) {
         if (!BaUser::validatePayPassword(\Yii::$app->user->id, $this->payPassword)) {
-            $this->addError($attribute, 'The payment password is incorrect');//支付密码不正确
+            $this->addError($attribute, Yii::t('app', 'paymentPasswordError'));
         }
     }
 
@@ -203,7 +204,7 @@ class BaApproachOrderForm extends Model
         $goods = ShopGoods::find()->select('ID,CATEGORY_TYPE')->where(['in', 'ID', $this->goodsId])->andWhere(['STATUS' => 1])->asArray()->all();
         $goodsCategoryType = array_unique(array_column($goods, 'CATEGORY_TYPE'));
         if (count($goodsCategoryType) != 1) {
-            $this->addError($attribute, 'Order cannot contain multiple product categories');//订单不能包含多种商品分类
+            $this->addError($attribute, Yii::t('app', 'orderCanNotContainMultipleProductCategories'));
         }
     }
 
@@ -211,46 +212,45 @@ class BaApproachOrderForm extends Model
      * 校验类型
      * @param $attribute
      */
-    public function isStatus($attribute)
-    {
-        if($this->type && !in_array($this->type, \Yii::$app->params['orderStatus'])){
-            $this->addError($attribute, '订单状态类型错误');
+    public function isStatus($attribute){
+        if(!in_array($this->type, \Yii::$app->params['orderStatus'])){
+            $this->addError($attribute, Yii::t('app', 'orderStatusTypeError'));
             return ;
         }
         if ($this->scenario == 'adminStatus'){
             if ($this->status == $this->_model['STATUS']) {
-                $this->addError($attribute, '订单状态没有改变');
+                $this->addError($attribute, Yii::t('app', 'orderStatusDoesNotChange'));
                 return ;
             }
             if($this->status == \Yii::$app->params['orderStatus']['notPaid'] && $this->_model['STATUS'] >= \Yii::$app->params['orderStatus']['delivery']) {
-                $this->addError($attribute, '订单已经进入物流状态不能改为未支付');
+                $this->addError($attribute, Yii::t('app', 'orderHasBeenLogisticsStatusDoesNotChangedUnpaid'));
                 return ;
             }
             elseif($this->status == \Yii::$app->params['orderStatus']['paid'] && $this->_model['STATUS'] >= \Yii::$app->params['orderStatus']['cancel']) {
-                $this->addError($attribute, '订单已失效不能处理');
+                $this->addError($attribute, Yii::t('app', 'orderHasBeenInvalidCanNotProcess'));
                 return ;
             }
             elseif($this->status == \Yii::$app->params['orderStatus']['delivery']) {
-                $this->addError($attribute, '订单不能单独处理为物流状态');
+                $this->addError($attribute, Yii::t('app', 'orderCanNotBeenChangedLogistics'));
                 return ;
             }
             elseif($this->status == \Yii::$app->params['orderStatus']['complete'] && $this->_model['STATUS'] > \Yii::$app->params['orderStatus']['cancel']) {
-                $this->addError($attribute, '订单已失效不能处理');
+                $this->addError($attribute, Yii::t('app', 'orderHasBeenInvalidCanNotProcess'));
                 return ;
             }
             elseif($this->status == \Yii::$app->params['orderStatus']['cancel']) {
                 if($this->_model['STATUS'] == \Yii::$app->params['orderStatus']['complete']) {
-                    $this->addError($attribute, '订单已完成不能取消');
+                    $this->addError($attribute, Yii::t('app', 'orderHasBeenFinishedCanNotCancel'));
                     return ;
                 }
                 if($this->_model['STATUS'] == \Yii::$app->params['orderStatus']['del']) {
-                    $this->addError($attribute, '订单已删除不能取消');
+                    $this->addError($attribute, Yii::t('app', 'orderHasBeenDeletedCanNotCancel'));
                     return ;
                 }
             }
             elseif($this->status == \Yii::$app->params['orderStatus']['del']) {
                 if($this->_model['STATUS'] == \Yii::$app->params['orderStatus']['complete']) {
-                    $this->addError($attribute, '订单已完成不能删除');
+                    $this->addError($attribute, Yii::t('app', 'orderHasBeenFinishedCanNotDelete'));
                     return ;
                 }
             }
@@ -276,7 +276,7 @@ class BaApproachOrderForm extends Model
             throw new Exception(Form::formatErrorsForApi($payload['message']));
         }
         if ($payload['data']['amount'] != $this->_model->PAY_AMOUNT * 100) {
-            throw new Exception(Form::formatErrorsForApi('支付金额与订单金额不符'));
+            throw new Exception(Form::formatErrorsForApi(Yii::t('app', 'payAmountNotEqualOrderAmount')));
         }
 
         // 订单类型:baOrder(BA订单)、baDec(BA报单)
@@ -316,7 +316,7 @@ class BaApproachOrderForm extends Model
 
                     // 修改会员锁定状态
                     if (!BaUser::updateAll(['STATUS' => 1], 'ID=:USER_ID', [':USER_ID' => $approachDecOrder['TO_USER_ID']])) {
-                        throw new Exception(Form::formatErrorsForApi('Update Brand Ambassador status error'));
+                        throw new Exception(Form::formatErrorsForApi(Yii::t('app', 'brandAmbassadorUpgradeError')));
                     }
                 }
                 BaApproachDecOrder::deleteAll('ORDER_SN = :ORDER_SN', [':ORDER_SN' => $this->sn]);
@@ -355,7 +355,7 @@ class BaApproachOrderForm extends Model
             if ($v) {
                 $goods = ShopGoods::findOneAsArray('ID = :ID AND STATUS = 1', [':ID' => $ids[$k]]);
                 if (!$goods) {
-                    throw new Exception('Products does not exists!');
+                    throw new Exception(Yii::t('app', 'productsDoesNotExists'));
                 }
                 if ($goods['STORE_NUMS'] > 0) {
                     if ($goods['TYPE'] == 1 || $goods['TYPE'] == 2) {
@@ -442,7 +442,7 @@ class BaApproachOrderForm extends Model
         if ($this->_address['PROVINCE'] != 1) {
             $warehouse = Region::getWarehouseByCode($this->_address['PROVINCE']);//仓库
             if (!$warehouse) {
-//                throw new Exception('地区2暂时不支持配送,具体联系客服');
+//                throw new Exception(Yii::t('app', 'deliveryTemporarilyNotSupported'));
             }
         }else{
             $warehouse = '01';

+ 8 - 7
common/models/forms/BaDeclarationLoopForm.php

@@ -4,6 +4,7 @@ namespace common\models\forms;
 use common\components\Model;
 use common\helpers\Form;
 use common\models\UserBind;
+use Yii;
 use yii\base\Exception;
 use yii\helpers\Json;
 use common\models\DeclarationPackage;
@@ -58,7 +59,7 @@ class BaDeclarationLoopForm extends Model
     public function formatData($attribute){
         //$this->data = Json::decode($this->data);
         if(!is_array($this->data)){
-            $this->addError($attribute, 'Data format error');// 数据格式错误
+            $this->addError($attribute,  Yii::t('app', 'dataFormatError'));
         }
     }
 
@@ -89,7 +90,7 @@ class BaDeclarationLoopForm extends Model
                 $model->recUserName = null;
                 $model->location = null;
             } else {
-                $this->addError($attribute, 'The format of the report data is incorrect');// 报单数据格式错误
+                $this->addError($attribute, Yii::t('app', 'reportFormatIncorrect'));
             }
 
         }
@@ -119,7 +120,7 @@ class BaDeclarationLoopForm extends Model
                     for ($i=0;$i<count($value['goodsId']);$i++){
                         $goods = ShopGoods::findOneAsArray('ID=:ID',[':ID'=> $value['goodsId'][$i]]);
                         if (!$goods) {
-                            throw new Exception('Products does not exists!');
+                            throw new Exception(Yii::t('app', 'productsDoesNotExists'));
                         }
                         if ($goods['STATUS'] == 1 ){
                             if($goods['STORE_NUMS'] >= $value['goodsNum'][$i]){
@@ -133,10 +134,10 @@ class BaDeclarationLoopForm extends Model
                                     $data->update();
                                 }
                             }else{
-                                throw new Exception($goods['GOODS_NAME'].'Insufficient inventory');// 商品库存不足
+                                throw new Exception($goods['GOODS_NAME']. Yii::t('app', 'insufficientInventory'));
                             }
                         }else{
-                            throw new Exception($goods['GOODS_NAME'].'Sold out');// 商品已下架
+                            throw new Exception($goods['GOODS_NAME']. Yii::t('app', 'soldOut'));
                         }
                     }
                 }
@@ -150,7 +151,7 @@ class BaDeclarationLoopForm extends Model
                         $allZcUserIds[] = null;
                         if($zcUserIdCard != null){
                             if($model->insertUserIdCard != $zcUserIdCard){
-                                throw new Exception('Bulk declaration member must be the same member'); // 批量报单会员必须是同一身份证
+                                throw new Exception(Yii::t('app', 'bulkDeclarationNotSames'));
                             }
                         } else {
                             $zcUserIdCard = $model->insertUserIdCard;
@@ -160,7 +161,7 @@ class BaDeclarationLoopForm extends Model
                         throw new Exception(Form::formatErrorsForApi($model->getErrors()));
                     }
                 } else {
-                    throw new Exception('The format of the report data is incorrect');// 报单数据格式错误
+                    throw new Exception(Yii::t('app', 'reportFormatIncorrect'));
                 }
             }
             $transaction->commit();

+ 3 - 2
common/models/forms/BaUserForm.php

@@ -9,6 +9,7 @@ use common\models\BaUser;
 use common\models\BaUserInfo;
 use common\models\User;
 use common\models\UserInfo;
+use Yii;
 use yii\db\Exception;
 
 /**
@@ -200,7 +201,7 @@ class BaUserForm extends Model
         $uid = \Yii::$app->user->id;
         $model = BaUser::findOne(['ID'=>$uid]);
         if ( !$model->validatePassword($this->oldPassword) ) {
-            $this->addError('modifyPassword', 'Original login password error');//原登录密码错误
+            $this->addError('modifyPassword', Yii::t('app', 'originalLoginPasswordError'));
             return false;
         }
         $model->PASSWORD_HASH = \Yii::$app->security->generatePasswordHash($this->password);
@@ -221,7 +222,7 @@ class BaUserForm extends Model
         $uid = \Yii::$app->user->id;
         $model = BaUser::findOne(['ID'=>$uid]);
         if ( !$model->validatePasswordPay($this->oldPassword) ) {
-            $this->addError('modifyPasswordPay', 'Original payment password error');//原支付密码错误
+            $this->addError('modifyPasswordPay', Yii::t('app', 'originalPaymentPasswordError'));
             return false;
         }
         $model->PAY_PASSWORD = \Yii::$app->security->generatePasswordHash($this->payPassword);

+ 16 - 15
common/models/forms/DeclarationForm.php

@@ -25,6 +25,7 @@ use common\models\User;
 use common\models\UserInfo;
 use common\models\UserNetwork;
 use common\models\UserRelation;
+use Yii;
 use yii\base\Exception;
 
 /**
@@ -191,7 +192,7 @@ class DeclarationForm extends Model
      */
     public function issetDec($attribute){
         if (!$decUser = User::find()->select('ID')->where('IS_DEC=1 AND USER_NAME=:USER_NAME', [':USER_NAME' => $this->decUserName])->asArray()->one()) {
-            $this->addError($attribute, 'Stockist does not exist'); // 报单中心不存在
+            $this->addError($attribute, Yii::t('app', 'stockistDoesNotExist'));
             return false;
         } else {
             if ($this->decType !== 'ba') {
@@ -376,7 +377,7 @@ class DeclarationForm extends Model
         else {
             if($this->insertUserName == $conUserName){
                // $this->addError('recUserName', '新加入的会员不存在');
-                $this->addError('recUserName', 'New member does not exist');
+                $this->addError('recUserName', Yii::t('app', 'newMemberDoesNotExist'));
                 return false;
             }
             // 去数据库里查找这个会员的所有上级
@@ -417,7 +418,7 @@ class DeclarationForm extends Model
         if($this->type == self::TYPE_ZC){
             if(!in_array($this->location, [1, 2, 3])){
                 //$this->addError($attribute, '市场必须在第1市场、第2市场或第三3市场');
-                $this->addError($attribute, 'Please select a market');
+                $this->addError($attribute, Yii::t('app', 'pleaseSelectMarket'));
             }
             $conUser = $recUser = null;
             if(isset($this->_tempParentUser[$this->conUserName])){
@@ -571,16 +572,16 @@ class DeclarationForm extends Model
         if(array_key_exists($this->type, $this->_types)){
             if($this->type == self::TYPE_ZC){
                 if($this->scenario == 'userDec'){
-                    if(!$this->insertUserName) $this->addError($attribute, 'Membership number must be filled in for initial purchase');//首购必须填写加入会员编号
+                    if(!$this->insertUserName) $this->addError($attribute, Yii::t('app', 'MembershipNumberFilledInitialPurchase'));
                 }
-                if(!$this->insertUserName) $this->addError($attribute, 'Membership number must be filled in for initial purchase');//首购必须填写加入会员编号
+                if(!$this->insertUserName) $this->addError($attribute, Yii::t('app', 'MembershipNumberFilledInitialPurchase'));
 //                if(!$this->insertUserIdCard) $this->addError($attribute, 'For the first purchase, the ID number of the member must be filled in');//首购必须填写加入会员的身份证号
-                if(!$this->conUserName) $this->addError($attribute, 'For the first purchase, you must fill in the instructor number of the member');//首购必须填写加入会员的指导老师编号
-                if(!$this->recUserName) $this->addError($attribute, 'For the first purchase, the Sponsor number of the member must be filled in');//首购必须填写加入会员的开拓人编号
-                if(!$this->location) $this->addError($attribute, 'The first purchase must be filled in the market of the member');//首购必须填写加入会员的市场
+                if(!$this->conUserName) $this->addError($attribute, Yii::t('app', 'fillTheInstructorNumberTheMember'));
+                if(!$this->recUserName) $this->addError($attribute, Yii::t('app', 'sponsorNumberMustBeFilled'));
+                if(!$this->location) $this->addError($attribute, Yii::t('app', 'beFilledTheMarketMember'));
             }
         } else {
-            $this->addError($attribute, 'Incorrect entry type');//报单类型不正确
+            $this->addError($attribute, Yii::t('app', 'incorrectEntryType'));
         }
     }
 
@@ -631,14 +632,14 @@ class DeclarationForm extends Model
         // 首购单,需要添加会员操作
         if($this->type == self::TYPE_ZC){
             if (preg_match("/[\x7f-\xff]/", $this->insertUserName)) {  //判断字符串中是否有中文
-                throw new Exception('Member number cannot contain Chinese characters');//会员编号中不能含有汉字
+                throw new Exception(Yii::t('app', 'memberNumberCanNotContainChineseCharacters'));
             }
             //报单商品及PV判断
             $decLevelConfig = Cache::getDecLevelConfig();
             $decLevel = $decLevelConfig[$this->decLv];
             $toDecLevel = $this->decLv;
             if(!$this->decLv){
-                throw new Exception('Please select the entry level');//请选择报单级别
+                throw new Exception(Yii::t('app', 'pleaseSelectTheEntryLevel'));
             }
             if($this->decWay==1) {
                 $decPackage = DeclarationPackage::findOneAsArray('ID=:ID', [':ID'=>$this->packageId]);
@@ -705,7 +706,7 @@ class DeclarationForm extends Model
                     }
                 }
                 if($totalPv<$decLevel['PERF']){
-                    throw new Exception('The total BV of self selected goods cannot be less than the BV of the selected entry level');//自选商品总BV不能小于所选报单级别BV
+                    throw new Exception(Yii::t('app', 'totalBVCanNotLessThanSelectedBV'));
                 }
                 foreach ($decLevelConfig as $key=>$val){
                     if($totalPv>=$val['PERF']){
@@ -713,7 +714,7 @@ class DeclarationForm extends Model
                     }
                 }
                 if($this->decLv!=$toDecLevel){
-                    throw new Exception('The total BV of self selected goods cannot exceed the BV value of the next level under the selected level');//自选商品总BV不能超过已选级别下一个级别的BV值
+                    throw new Exception(Yii::t('app', 'totalBVCanNotLessThanNextSelectedLevelBV'));
                 }
                 $this->_decAmount = $totalAmount;
                 $this->_decPv = $totalPv;
@@ -723,7 +724,7 @@ class DeclarationForm extends Model
             //看现金余额是否充足
             $loginUserId = \Yii::$app->user->id;
             if (Cash::getAvailableBalance($loginUserId) < $this->_decAmount){
-                throw new Exception('The applicant is short of cash and cannot complete the declaration');//报单人现金不足,无法完成报单
+                throw new Exception(Yii::t('app', 'applicantCashShort'));
             }
 
             if(!($zcResult = $this->addUser($allData))) {
@@ -845,7 +846,7 @@ class DeclarationForm extends Model
         if($this->province!=1){
             $warehouse = Region::getWarehouseByCode($this->province);//仓库
             if(!$warehouse){
-                throw new Exception('Delivery is temporarily not supported in the region. Contact customer service for details');//地区暂时不支持配送,具体联系客服
+                throw new Exception(Yii::t('app', 'deliveryTemporarilyNotSupported'));
             }
         }else{
             $warehouse = '01';

+ 9 - 8
common/models/forms/DeclarationLoopForm.php

@@ -4,6 +4,7 @@ namespace common\models\forms;
 use common\components\Model;
 use common\helpers\Form;
 use common\models\UserBind;
+use Yii;
 use yii\base\Exception;
 use yii\helpers\Json;
 use common\models\DeclarationPackage;
@@ -90,7 +91,7 @@ class DeclarationLoopForm extends Model
                 $model->location = null;
                 $model->decType = null;
             } else {
-                $this->addError($attribute, 'The format of the report data is incorrect');// 报单数据格式错误
+                $this->addError($attribute, Yii::t('app', 'reportFormatIncorrect'));
             }
 
         }
@@ -126,11 +127,11 @@ class DeclarationLoopForm extends Model
 
                     $packagedata = DeclarationPackage::findOneAsArray('ID=:ID', [':ID' => $value['packageId']]);
                     if (!$packagedata) {
-                        throw new Exception('Products does not exists!');
+                        throw new Exception(Yii::t('app', 'productsDoesNotExists'));
                     }
                     //var_dump($packagedata['ID']);
                     if($packagedata['STORE_NUMS']<=0){
-                        throw new Exception($packagedata['PACKAGE_NAME'].'Insufficient inventory');// 库存不足
+                        throw new Exception($packagedata['PACKAGE_NAME'] . Yii::t('app', 'insufficientInventory'));
                     }
                 }
 
@@ -138,14 +139,14 @@ class DeclarationLoopForm extends Model
                     for ($i=0;$i<count($value['goodsId']);$i++){
                         $goods = ShopGoods::findOneAsArray('ID=:ID',[':ID'=> $value['goodsId'][$i]]);
                         if (!$goods) {
-                            throw new Exception('Products does not exists!');
+                            throw new Exception(Yii::t('app', 'productsDoesNotExists'));
                         }
                         if ($goods['STATUS'] == 1 ){
                             if($goods['STORE_NUMS'] < $value['goodsNum'][$i]){
-                                throw new Exception($goods['GOODS_NAME'].'Insufficient inventory');// 商品库存不足
+                                throw new Exception($goods['GOODS_NAME'] . Yii::t('app', 'insufficientInventory'));
                             }
                         }else{
-                            throw new Exception($goods['GOODS_NAME'].'Sold out');// 商品已下架
+                            throw new Exception($goods['GOODS_NAME'] . Yii::t('app', 'soldOut'));
                         }
                     }
                 }
@@ -159,7 +160,7 @@ class DeclarationLoopForm extends Model
                         $allZcUserIds[] = null;
                         if($zcUserIdCard != null){
                             if($model->insertUserIdCard != $zcUserIdCard){
-                                throw new Exception('Bulk declaration member must be the same member');//批量报单会员必须是同一身份证
+                                throw new Exception(Yii::t('app', 'bulkDeclarationNotSames'));
                             }
                         } else {
                             $zcUserIdCard = $model->insertUserIdCard;
@@ -169,7 +170,7 @@ class DeclarationLoopForm extends Model
                         throw new Exception(Form::formatErrorsForApi($model->getErrors()));
                     }
                 } else {
-                    throw new Exception('The format of the report data is incorrect');// 报单数据格式错误
+                    throw new Exception(Yii::t('app', 'reportFormatIncorrect'));
                 }
             }
 

+ 10 - 9
common/models/forms/DeclarationUpgradeForm.php

@@ -22,6 +22,7 @@ use common\models\ShopGoods;
 use common\models\User;
 use common\models\UserInfo;
 use common\models\UserNetwork;
+use Yii;
 use yii\base\Exception;
 
 /**
@@ -117,13 +118,13 @@ class DeclarationUpgradeForm extends Model
             $decLevel = $decLevelConfig[$this->decLv];
             $toDecLevel = $this->decLv;
             if(!$this->decLv){
-                throw new Exception('Please select upgrade level');//请选择升级级别
+                throw new Exception(Yii::t('app', 'pleaseSelectUpgradeLevel'));
             }
             $baseInfo = Info::baseInfoZhByUserName($this->insertUserName);
             $userId = $baseInfo['ID'];
             $userDecPvSum = User::sumDevPvByUserId($userId); // 用户所有报单PV总和
             if ($userDecPvSum != $this->nowPerf) {
-                throw new Exception('Please contact the customer service personnel to check the performance of upgraded members');//请联系客服人员核对升级会员业绩
+                throw new Exception(Yii::t('app', 'checkPerformanceOfUpgradedMember'));
             }
             // 获取用户是否是观察期
             $observe = Config::getConfigByType('observe'); // 获取观察期配置信息
@@ -131,7 +132,7 @@ class DeclarationUpgradeForm extends Model
             $isObserve = User::checkIsObserve($baseInfo['CREATED_AT'], $observeLimit); // 判断用户是否再观察期中
             $diffPerf = $isObserve ? $this->nowPerf : 0; // 观察期内升级要加上用户累计的PV,全额则基础PV为0,全额购买
             if ($this->decWay != 2) {
-                throw new Exception('The upgrade method is incorrect. Please contact the customer service personnel');//升级方式不正确,请联系客服人员
+                throw new Exception(Yii::t('app', 'upgradeMethodIncorrect'));
             }
             if($this->decWay==1) {
                 // 先不加套餐升级方式
@@ -187,7 +188,7 @@ class DeclarationUpgradeForm extends Model
                 // 这里特殊是用户原报单PV之和+用户购买的商品总PV
                 $checkPv = $totalPv + $diffPerf;
                 if($checkPv < $decLevel['PERF']) {
-                    throw new Exception('Total PV cannot be less than the selected level PV');//总PV不能小于所选级别PV
+                    throw new Exception(Yii::t('app', 'totalPVLessThan'), 400);
                 }
                 foreach ($decLevelConfig as $key=>$val){
                     if($checkPv>=$val['PERF']){
@@ -195,7 +196,7 @@ class DeclarationUpgradeForm extends Model
                     }
                 }
                 if($this->decLv!=$toDecLevel){
-                    throw new Exception('The total PV cannot exceed the PV value of the next level under the selected level');//总PV不能超过已选级别下一个级别的PV值
+                    throw new Exception(Yii::t('app', 'totalPvExceedPv'), 400);
                 }
                 $this->_decAmount = $totalAmount;
                 $this->_decPv = $totalPv;
@@ -204,14 +205,14 @@ class DeclarationUpgradeForm extends Model
             }
             //看现金余额是否充足
             if (Cash::getAvailableBalance($loginUserId) < $this->_decAmount){
-                throw new Exception('The applicant is short of cash and cannot complete the declaration');//报单人现金不足,无法完成报单
+                throw new Exception(Yii::t('app', 'applicantCashShort'), 400);
             }
             $baseInfo = Info::baseInfoZhByUserName($this->insertUserName);
             $this->_insertUserId = $baseInfo['ID']; // 被报单人,通过insername 查找用户id
             $insertConId = $baseInfo['CON_UID'];
             $insertRecId = $baseInfo['REC_UID'];
             if(!($decResult = $this->addDecOrder($insertConId,$insertRecId, $baseInfo['DEC_LV'],$isObserve,$this->remark))) {
-                throw new Exception("operation failed");//操作失败
+                throw new Exception(Yii::t('app', 'failed'), 400);
             }
         }
         return true;
@@ -225,7 +226,7 @@ class DeclarationUpgradeForm extends Model
     public function addDecOrder($insertConId,$insertRecId,$oriDecLv,$isObserve,$remark=''){
         $warehouse = Region::getWarehouseByCode($this->province);//仓库
         if(!$warehouse){
-            throw new Exception('Delivery is temporarily not supported in the region. Contact customer service for details');//地区暂时不支持配送,具体联系客服
+            throw new Exception(Yii::t('app', 'deliveryTemporarilyNotSupported'), 400);
         }
         $upgradeType = $isObserve ? 1 : 2; // 1补差  2全额
         $periodObj = Period::instance();
@@ -370,7 +371,7 @@ class DeclarationUpgradeForm extends Model
             $modifyDecLv = $decLevelLog->frontendChange($decLog);
             if (empty($modifyDecLv)) {
                 $transaction->rollBack();
-                throw new Exception("Failed to upgrade for member");//为会员升级失败
+                throw new Exception(Yii::t('app', 'failedToUpgrade'), 400);
             }
             $transaction->commit();
         } catch(Exception $e) {

+ 3 - 0
common/models/forms/LogUserLoginForm.php

@@ -21,6 +21,7 @@ class LogUserLoginForm extends \yii\base\Model
     public $device;
     public $request_route;
     public $return_result;
+    public $version;
     /**
      * DB
      * @return \yii\db\Connection the database connection used by this AR class.
@@ -74,6 +75,7 @@ class LogUserLoginForm extends \yii\base\Model
             'device' => '客户端',
             'request_route' => '请求路径',
             'return_result' => '返回内容',
+            'version' => '版本',
         ];
     }
 
@@ -97,6 +99,7 @@ class LogUserLoginForm extends \yii\base\Model
         $model->device = $this->device;
         $model->request_route = $this->request_route;
         $model->return_result = $this->return_result;
+        $model->version = $this->version;
 
         if(!$model->save()){
             return false;

+ 88 - 93
common/models/forms/OrderForm.php

@@ -21,6 +21,7 @@ use common\models\User;
 use common\models\UserNetwork;
 use common\models\RemainPv;
 use common\models\FlowRemainPv;
+use Yii;
 use yii\base\Exception;
 
 /**
@@ -92,26 +93,26 @@ class OrderForm extends Model
     public function attributeLabels()
     {
         return [
-            'sn' => 'Order Code', // 订单号
-            'expressCompany' => '快递公司',
-            'orderTrackNo' => '快递单号',
-            'status' => 'State', // 状态
-            'remark' => 'Remark', // 备注
-            'type' => 'Order Type',// 订单类型
-            'addressId' => 'Shipping address',// 收货地址
-            'payType' => 'Pay Type',// 支付方式
-            'goodsId' => 'Product ID',//商品ID
-            'goodsNum' => 'Product quantity',//商品数量
-            'userName' => 'Repeat sales Member No. does not exist',//复消会员编号
-            'consignee' => 'Consignee',// 收货人
-            'acceptMobile' => 'Accept Mobile',// 收货电话
-            'province' => 'Province',// 省
-            'city' => 'City',// 市
-            'county' => 'County',// 区
-            'lgaName' => 'Lga Name',
-            'cityName' => 'City Name',
-            'detailaddress' => 'Address',// 收货详细地址
-            'email' => 'Email',
+            'sn' => Yii::t('app', 'orderSn'),
+            'expressCompany' => Yii::t('app', 'expressCompany'),
+            'orderTrackNo' => Yii::t('app', 'orderTrackNo'),
+            'status' => Yii::t('app', 'state'),
+            'remark' => Yii::t('app', 'remark'),
+            'type' => Yii::t('app', 'orderType'),
+            'addressId' => Yii::t('app', 'shippingAddress'),
+            'payType' => Yii::t('app', 'payType'),
+            'goodsId' => Yii::t('app', 'productID'),
+            'goodsNum' => Yii::t('app', 'quantity'),
+            'userName' => Yii::t('app', 'repeatSalesMemberNo'),
+            'consignee' => Yii::t('app', 'consignee'),
+            'acceptMobile' => Yii::t('app', 'acceptMobile'),
+            'province' => Yii::t('app', 'province'),
+            'city' => Yii::t('app', 'city'),
+            'county' => Yii::t('app', 'county'),
+            'lgaName' =>Yii::t('app', 'lgaName'),
+            'cityName' => Yii::t('app', 'cityName'),
+            'detailaddress' => Yii::t('app', 'detailAddress'),
+            'email' => Yii::t('app', 'email'),
         ];
     }
 
@@ -152,36 +153,36 @@ class OrderForm extends Model
         if ($this->sn) {
             $this->_model = Order::findOne(['SN'=>$this->sn]);
             if (!$this->_model){
-                $this->addError('sn', '订单不存在');
+                $this->addError('sn', Yii::t('app', 'orderDoesNotExist'));
                 return false;
             }
         }
         if ($this->scenario == 'adminDelivery'){
             if($this->_model['STATUS'] == \Yii::$app->params['orderStatus']['cancel']) {
-                $this->addError('sn', '订单已取消不能发货');
+                $this->addError('sn', Yii::t('app', 'orderHasBeenCancelCanNotDeliver'));
                 return false;
             }
             if($this->_model['STATUS'] == \Yii::$app->params['orderStatus']['del']) {
-                $this->addError('sn', '订单已删除不能发货');
+                $this->addError('sn', Yii::t('app', 'orderHasBeenDeleteCanNotDeliver'));
                 return false;
             }
         }
 
         if ($this->scenario == 'adminRefund'){
             if ($this->_model['STATUS'] != \Yii::$app->params['orderStatus']['paid']['value']) {
-                $this->addError('sn', '订单状态支付状态不支持退款');
+                $this->addError('sn', Yii::t('app', 'orderPayStatusDoesNotSupportRefund'));
                 return false;
             }
             if ($this->_model['DELIVERY_STATUS'] != \Yii::$app->params['deliveryStatus']['notDelivery']['value']) {
-                $this->addError('sn', '订单物流状态不支持退款');
+                $this->addError('sn', Yii::t('app', 'orderLogisticsStatusDoesNotSupportRefund'));
                 return false;
             }
             if ($this->_model['PAY_TYPE'] != 'pay_stack') {
-                $this->addError('sn', '订单支付方式不支持退款');
+                $this->addError('sn', Yii::t('app', 'orderPayTypeDoesNotSupportRefund'));
                 return false;
             }
             if (!$this->_model['REMARK']) {
-                $this->addError('sn', '订单支付信息不存在');
+                $this->addError('sn', Yii::t('app', 'paymentInfoDoesNotExists'));
                 return false;
             }
         }
@@ -196,7 +197,7 @@ class OrderForm extends Model
      */
     public function validatePassword($attribute, $params) {
         if (!User::validatePayPassword(\Yii::$app->user->id, $this->payPassword)) {
-            $this->addError($attribute, 'The payment password is incorrect');//支付密码不正确
+            $this->addError($attribute, Yii::t('app', 'paymentPasswordError'));
         }
     }
 
@@ -206,7 +207,7 @@ class OrderForm extends Model
      */
     public function isAddress($attribute){
         if (!$receiveAddress = ReceiveAddress::find()->where(' ID=:ID', [':ID' => $this->addressId])->asArray()->one()) {
-            $this->addError($attribute, '收货地址不存在');
+            $this->addError($attribute, Yii::t('app', 'shippingDoesNotExist'));
         } else {
             $this->_address = $receiveAddress;
         }
@@ -219,26 +220,26 @@ class OrderForm extends Model
      */
     public function isPayType($attribute){
         if(!array_key_exists($this->payType, ShopGoods::payTypes())){
-            $this->addError($attribute, '支付方式错误1');
+            $this->addError($attribute, Yii::t('app', 'payTypeError'));
             return;
         }
 
         // 一个订单只能包含一类商品
         $goods = ShopGoods::find()->select('ID,CATEGORY_TYPE')->where(['in', 'ID', $this->goodsId])->andWhere(['STATUS' => 1])->asArray()->all();
         if (!$goods) {
-            throw new Exception('Products does not exists!');
+            throw new Exception(Yii::t('app', 'productsDoesNotExists'));
             return;
         }
         $goodsCategoryType = array_unique(array_column($goods, 'CATEGORY_TYPE'));
         if (count($goodsCategoryType) > 1) {
-            $this->addError($attribute, 'Order cannot contain multiple product categories'); // 订单不能包含多种商品分类
+            $this->addError($attribute, Yii::t('app', 'orderCanNotHasMoreClassification'));
             return;
         }
 
         // 购买方式
         $sellTypeLabelMap = array_column(ShopGoods::SALE_TYPE, NULL, 'label');
         if (!array_key_exists($this->payType, $sellTypeLabelMap)) {
-            $this->addError($attribute, '支付方式错误2');
+            $this->addError($attribute, Yii::t('app', 'payTypeError'));
             return;
         }
 
@@ -247,13 +248,13 @@ class OrderForm extends Model
         // 商品类型
         $currCategoryType = $goodsCategoryType[0];
         if (!array_key_exists($currCategoryType, $categoryType)) {
-            $this->addError($attribute, '商品分类错误');
+            $this->addError($attribute, Yii::t('app', 'shopGoodClassificationError'));
             return;
         }
 
         $sellType = $categoryType[$currCategoryType]['sell_type'] ?? [];
         if (!$sellType || !in_array($this->payType, array_column($sellType, 'label'))) {
-            $this->addError($attribute, '支付方式错误3');
+            $this->addError($attribute, Yii::t('app', 'payTypeError'));
             return;
         }
     }
@@ -264,43 +265,43 @@ class OrderForm extends Model
      */
     public function isStatus($attribute){
         if(!in_array($this->type, \Yii::$app->params['orderStatus'])){
-            $this->addError($attribute, '类型错误');
+            $this->addError($attribute, Yii::t('app', 'orderStatusTypeError'));
             return ;
         }
         if ($this->scenario == 'adminStatus'){
             if ($this->status == $this->_model['STATUS']) {
-                $this->addError($attribute, '订单状态没有改变');
+                $this->addError($attribute, Yii::t('app', 'orderStatusDoesNotChange'));
                 return ;
             }
             if($this->status == \Yii::$app->params['orderStatus']['notPaid'] && $this->_model['STATUS'] >= \Yii::$app->params['orderStatus']['delivery']) {
-                $this->addError($attribute, '订单已经进入物流状态不能改为未支付');
+                $this->addError($attribute, Yii::t('app', 'orderHasBeenLogisticsStatusDoesNotChangedUnpaid'));
                 return ;
             }
             elseif($this->status == \Yii::$app->params['orderStatus']['paid'] && $this->_model['STATUS'] >= \Yii::$app->params['orderStatus']['cancel']) {
-                $this->addError($attribute, '订单已失效不能处理');
+                $this->addError($attribute, Yii::t('app', 'orderHasBeenInvalidCanNotProcess'));
                 return ;
             }
             elseif($this->status == \Yii::$app->params['orderStatus']['delivery']) {
-                $this->addError($attribute, '订单不能单独处理为物流状态');
+                $this->addError($attribute, Yii::t('app', 'orderCanNotBeenChangedLogistics'));
                 return ;
             }
             elseif($this->status == \Yii::$app->params['orderStatus']['complete'] && $this->_model['STATUS'] > \Yii::$app->params['orderStatus']['cancel']) {
-                $this->addError($attribute, '订单已失效不能处理');
+                $this->addError($attribute, Yii::t('app', 'orderHasBeenInvalidCanNotProcess'));
                 return ;
             }
             elseif($this->status == \Yii::$app->params['orderStatus']['cancel']) {
                 if($this->_model['STATUS'] == \Yii::$app->params['orderStatus']['complete']) {
-                    $this->addError($attribute, '订单已完成不能取消');
+                    $this->addError($attribute, Yii::t('app', 'orderHasBeenFinishedCanNotCancel'));
                     return ;
                 }
                 if($this->_model['STATUS'] == \Yii::$app->params['orderStatus']['del']) {
-                    $this->addError($attribute, '订单已删除不能取消');
+                    $this->addError($attribute, Yii::t('app', 'orderHasBeenDeletedCanNotCancel'));
                     return ;
                 }
             }
             elseif($this->status == \Yii::$app->params['orderStatus']['del']) {
                 if($this->_model['STATUS'] == \Yii::$app->params['orderStatus']['complete']) {
-                    $this->addError($attribute, '订单已完成不能删除');
+                    $this->addError($attribute, Yii::t('app', 'orderHasBeenFinishedCanNotDelete'));
                     return ;
                 }
             }
@@ -356,7 +357,7 @@ class OrderForm extends Model
             throw new Exception(Form::formatErrorsForApi($payload['message']));
         }
         if ($payload['data']['amount'] != $this->_model->PAY_AMOUNT * 100) {
-            throw new Exception(Form::formatErrorsForApi('支付金额与订单金额不符'));
+            throw new Exception(Form::formatErrorsForApi(Yii::t('app', 'payAmountNotEqualOrderAmount')));
         }
 
         $db = \Yii::$app->db;
@@ -393,7 +394,7 @@ class OrderForm extends Model
         $remark = $this->_model->REMARK ? json_decode($this->_model->REMARK, true) : [];
         $reference = $remark['reference'] ?? '';
         if (!$reference) {
-            throw new Exception(Form::formatErrorsForApi('支付信息不存在'));
+            throw new Exception(Form::formatErrorsForApi(Yii::t('app', 'paymentInfoDoesNotExists')));
         }
         // 退款金额. 订单支付金额 * 100
         $amount = $this->_model->PAY_AMOUNT * 100;
@@ -467,7 +468,7 @@ class OrderForm extends Model
             if ($v) {
                 $goods = ShopGoods::findOneAsArray('ID=:ID AND STATUS=1',[':ID'=> $ids[$k]]);
                 if (!$goods) {
-                    throw new Exception('Products does not exists!');
+                    throw new Exception(Yii::t('app', 'productsDoesNotExists'));
                 }
                 if($goods['STORE_NUMS']>0){
                     if ($goods['TYPE'] == 1 || $goods['TYPE'] == 2) {
@@ -569,7 +570,7 @@ class OrderForm extends Model
                 if ($v){
                     $goods = ShopGoods::findOneAsArray('ID=:ID AND STATUS=1',[':ID'=> $ids[$k]]);
                     if (!$goods) {
-                        throw new Exception('Products does not exists!');
+                        throw new Exception(Yii::t('app', 'productsDoesNotExists'));
                     }
                     if ($goods['STORE_NUMS'] >= $this->goodsNum[$k]){
                         $data = ShopGoods::find()->where(['ID' => $ids[$k]])->one();
@@ -584,7 +585,7 @@ class OrderForm extends Model
 
                         }
                     }else{
-                        throw new Exception($goods['GOODS_NAME'].'库存不足,无法购买商品');
+                        throw new Exception($goods['GOODS_NAME'] . Yii::t('app', 'insufficientInventory'));
 
                     }
                 }
@@ -617,19 +618,19 @@ class OrderForm extends Model
 
         if ($payType == 'cash') {
             if (Cash::getAvailableBalance($loginUserId) < $payAmount) {
-                return ['code' => 500, 'message' => '余额不足,无法购买商品'];
+                return ['code' => 500, 'message' => Yii::t('app', 'cashDoesNotAdequate')];
             }
         } else if ($payType =='exchange') {
             if ($payAmount > Balance::getBalanceExchangePoints($loginUserId)) {
-                return ['code' => 500, 'message' => '兑换积分不足,无法购买商品'];
+                return ['code' => 500, 'message' => Yii::t('app', 'exchangePointDoesNotAdequate')];
             }
         } else if ($payType == 'tourism_points') {
             if ($payAmount > Balance::getBalanceTourism($loginUserId)) {
-                return ['code' => 500, 'message' => '旅游积分不足,无法购买商品'];
+                return ['code' => 500, 'message' => Yii::t('app', 'travelPointDoesNotAdequate')];
             }
         } else if ($payType == 'garage_points') {
             if ($payAmount > Balance::getBalanceGarage($loginUserId)) {
-                return ['code' => 500, 'message' => '车房积分不足,无法购买商品'];
+                return ['code' => 500, 'message' => Yii::t('app', 'carFundPointDoesNotAdequate')];
             }
 //        } else{
 //            if ($payAmount > Balance::getBalanceReconsumePoints($loginUserId)) {
@@ -660,7 +661,7 @@ class OrderForm extends Model
         if ($this->_address['PROVINCE'] != 1) {
             $warehouse = Region::getWarehouseByCode($this->_address['PROVINCE']);//仓库
             if (!$warehouse) {
-                throw new Exception('地区2暂时不支持配送,具体联系客服');
+                throw new Exception(Yii::t('app', 'deliveryTemporarilyNotSupported'));
             }
         }else{
             $warehouse = '01';
@@ -852,55 +853,49 @@ class OrderForm extends Model
                 $userId = Info::getUserIdByUserName($this->userName);
                 $userNetwork = UserNetwork::find()->where("USER_ID=:USER_ID AND INSTR(PARENT_UIDS,'{$loginUserId}')>0", ['USER_ID'=>$userId])->count();
                 if(!$userNetwork){
-                    throw new Exception($this->userName.'不是您的伞下会员,不能为其复消!');
+                    throw new Exception($this->userName . Yii::t('app', 'doesNotYourSubMemberCanNotReconsume'));
                 }
             }
             //判断用户余额是否充足
             if($this->payType=='cash') {
                 if (Cash::getAvailableBalance($loginUserId) < $this->_payAmount) {
-                    throw new Exception('余额不足,无法购买商品');
+                    throw new Exception(Yii::t('app', 'cashDoesNotAdequate'));
                 }
             }else{
                 if ($this->_payAmount > Balance::getBalanceReconsumePoints($loginUserId)) {
-                    throw new Exception('复消积分不足,无法购买商品');
+                    throw new Exception(Yii::t('app', 'exchangePointDoesNotAdequate'));
                 }
             }
 
-
-
 			/**
-			             * 2022-04-28
-			             * York
-			             * 支付后减少库存
-			             */
-			            foreach ($this->goodsNum as $k => $v){
-			                if ($v){
-			                    $goods = ShopGoods::findOneAsArray('ID=:ID AND STATUS=1',[':ID'=> $ids[$k]]);
-                                if (!$goods) {
-                                    throw new Exception('Products does not exists!');
-                                }
-			                    if ($goods['STORE_NUMS'] >= $this->goodsNum[$k]){
-			                        $data = ShopGoods::find()->where(['ID' => $ids[$k]])->one();
-			                        $goods_store_nums = $data->STORE_NUMS - $this->goodsNum[$k];
-			                        $data->STORE_NUMS = $goods_store_nums;
-			                        $data->update();
-			                        //下单后库存小于等于0 商品下架
-			                        if($goods_store_nums <= 0){
-			                            $data->STATUS = 0;
-			                            $data->UPDATED_AT = Date::nowTime();
-			                            $data->update();
-
-			                        }
-			                    }else{
-			                        throw new Exception($goods['GOODS_NAME'].'库存不足,无法购买商品');
-
-			                    }
-
-
-			                }
-			            }
-			//                exit();
+             * 2022-04-28
+             * York
+             * 支付后减少库存
+             */
+            foreach ($this->goodsNum as $k => $v){
+                if ($v){
+                    $goods = ShopGoods::findOneAsArray('ID=:ID AND STATUS=1',[':ID'=> $ids[$k]]);
+                    if (!$goods) {
+                        throw new Exception(Yii::t('app', 'productsDoesNotExists'));
+                    }
+                    if ($goods['STORE_NUMS'] >= $this->goodsNum[$k]){
+                        $data = ShopGoods::find()->where(['ID' => $ids[$k]])->one();
+                        $goods_store_nums = $data->STORE_NUMS - $this->goodsNum[$k];
+                        $data->STORE_NUMS = $goods_store_nums;
+                        $data->update();
+                        //下单后库存小于等于0 商品下架
+                        if($goods_store_nums <= 0){
+                            $data->STATUS = 0;
+                            $data->UPDATED_AT = Date::nowTime();
+                            $data->update();
+
+                        }
+                    }else{
+                        throw new Exception($goods['GOODS_NAME'] . Yii::t('app', 'insufficientInventory'));
 
+                    }
+                }
+            }
 
             //写入订单
             if (!$orderResult = $this->addUserOrder()) {
@@ -933,7 +928,7 @@ class OrderForm extends Model
         if($this->province!=1){
             $warehouse = Region::getWarehouseByCode($this->province);//仓库
             if(!$warehouse){
-                throw new Exception('地区1暂时不支持配送,具体联系客服');
+                throw new Exception(Yii::t('app', 'deliveryTemporarilyNotSupported'));
             }
         }else{
             $warehouse = '01';
@@ -980,9 +975,9 @@ class OrderForm extends Model
 
         //扣除会员余额/积分
         if($this->payType=='cash') {
-            Cash::changeUserCash($loginUserId, 'CASH', -abs($this->_payAmount), ['REMARK' => 'Members reselling balance payment']); // 会员复销余额支付
+            Cash::changeUserCash($loginUserId, 'CASH', -abs($this->_payAmount), ['REMARK' => Yii::t('app', 'membersResellingBalancePayment')]);
         }else{
-            Balance::changeUserBonus($loginUserId,'reconsume_points', -abs($this->_payAmount),['DEAL_TYPE_ID' => DealType::RECONSUME_POINTS_EXCHANGE, 'REMARK' => '会员复销积分兑换']);
+            Balance::changeUserBonus($loginUserId,'reconsume_points', -abs($this->_payAmount),['DEAL_TYPE_ID' => DealType::RECONSUME_POINTS_EXCHANGE, 'REMARK' => Yii::t('app', 'membersExchangePointPayment')]);
         }
         return $orderModel;
     }

+ 5 - 5
common/models/forms/PeriodForm.php

@@ -73,11 +73,11 @@ class PeriodForm extends Model
                 $this->addError($attributes, '所传期数不可封期');
             }
         }
-        elseif($this->scenario == 'perf') {
-            if(!$periodObj->isCanPerf($this->periodNum)){
-                $this->addError($attributes, '所传期数不可生成业绩单');
-            }
-        }
+//        elseif($this->scenario == 'perf') {
+//            if(!$periodObj->isCanPerf($this->periodNum)){
+//                $this->addError($attributes, '所传期数不可生成业绩单');
+//            }
+//        }
         elseif($this->scenario == 'calc') {
             if(!$periodObj->isCanCalc($this->periodNum)){
                 $this->addError($attributes, '所传期数不可结算');

+ 3 - 2
common/models/forms/TransferForm.php

@@ -26,6 +26,7 @@ use common\models\UserBonus;
 use common\models\UserInfo;
 use common\models\UserRelation;
 use common\models\UserSystem;
+use Yii;
 use yii\base\Exception;
 use yii\helpers\Json;
 
@@ -299,7 +300,7 @@ class TransferForm extends Model {
             return null;
         }
         if ($this->_fromUserInfo['ID']==$this->_toUserInfo['ID']){
-            throw new \Exception('Not allowed to transfer to yourself');
+            throw new \Exception(Yii::t('app', 'notAllowedToTransferToYourself'));
         }
         $fromData = Balance::getLogData($this->_fromUserInfo['ID']);
         $toData = Balance::getLogData($this->_toUserInfo['ID']);
@@ -350,7 +351,7 @@ class TransferForm extends Model {
                 Cash::changeUserCash($this->_fromUserInfo['ID'], 'CASH', -abs($this->amount), ['TRANSFER_SN' => $model->TRANSFER_SN,'DEAL_TYPE_ID' => DealType::TRANSFER_OUT, 'REMARK' => 'To:' . $this->_toUserInfo['USER_NAME'] . ',' . $this->remark]);
                 Cash::changeUserCash($this->_toUserInfo['ID'], 'CASH', abs($amount), ['TRANSFER_SN' => $model->TRANSFER_SN,'DEAL_TYPE_ID' => DealType::TRANSFER_IN, 'REMARK' => 'From:' . $this->_fromUserInfo['USER_NAME'] . ',' . $this->remark]);
             }else {
-                throw new \Exception('Wrong transaction type'); // 错误的交易类型
+                throw new \Exception(Yii::t('app', 'wrongTransactionType'));
             }
 
             $transaction->commit();

+ 4 - 3
common/models/forms/UploadForm.php

@@ -179,10 +179,11 @@ class UploadForm extends Model {
                 unlink($this->file->tempName);
             } else {
                 // 生成文件名
-//                $fileName = Tool::generateId(false);
-                $fileName = $this->file->baseName;
+                $fileName = Tool::generateId(false);
+//                $fileName = $this->file->baseName;
                 // 保存在本地
-                $localPath = '/ng/Volumes/HDD/workshop/old/ar.upload.ming/files/' . $fileName . '.' . $this->file->extension;
+                $localPath =  \Yii::$app->params['localUpload']['dns'] . $fileName . '.' . $this->file->extension;
+//                $localPath = '/ng-stage/Volumes/HDD/workshop/old/ar.upload.ming/files/' . $fileName . '.' . $this->file->extension;
                 if (!$this->file->saveAs($localPath)) {
                     throw new Exception('Failed');
                 }

+ 10 - 9
common/models/forms/UserConfigForm.php

@@ -14,6 +14,7 @@ use common\models\DealType;
 use common\models\Period;
 use common\models\User;
 use common\models\UserInfo;
+use Yii;
 use yii\db\Exception;
 
 /**
@@ -46,8 +47,8 @@ class UserConfigForm extends Model {
 
     public function attributeLabels() {
         return [
-            'isAutoWithdraw' => '是否自动提现',
-            'allowReconsumeSms' => '开启复销短信通知',
+            'isAutoWithdraw' => Yii::t('app', 'isAutoWithdraw'),
+            'allowReconsumeSms' => Yii::t('app', 'allowReconsumeSms'),
         ];
     }
 
@@ -71,11 +72,11 @@ class UserConfigForm extends Model {
      */
     public function isCanAutoWithdraw($attribute) {
         if (!Info::isVerified(\Yii::$app->user->id)) {
-            $this->addError($attribute, '请先实名认证');
+            $this->addError($attribute, Yii::t('app', 'pleaseAuthentication'));
         }
         $this->_config = Cache::getSystemConfig();
         if (!$this->_config['isCanAutoWithdraw']['VALUE'] && $this->isAutoWithdraw) {
-            $this->addError('scenario', '系统关闭了自动提现');
+            $this->addError('scenario', Yii::t('app', 'systemCloseAutoWithdraw'));
         }
     }
 
@@ -86,7 +87,7 @@ class UserConfigForm extends Model {
     public function isAllowReconsumeSms($attribute) {
         $this->_config = Cache::getSystemConfig();
         if (!$this->_config['smsOpen']['VALUE'] && $this->isAutoWithdraw) {
-            $this->addError('scenario', '系统关闭了开启复销短信通知');
+            $this->addError('scenario', Yii::t('app', 'systemCloseReconsumeSMSNotification'));
         }
     }
 
@@ -103,7 +104,7 @@ class UserConfigForm extends Model {
         $transaction = \Yii::$app->db->beginTransaction();
         try {
             if (!UserInfo::updateAll(['IS_AUTO_WITHDRAW' => $this->isAutoWithdraw], 'USER_ID=:USER_ID', [':USER_ID' => \Yii::$app->user->id])) {
-                throw new Exception('更新个人设置失败');
+                throw new Exception(Yii::t('app', 'updateUserConfigFailed'));
             }
             $transaction->commit();
         } catch (Exception $e) {
@@ -139,7 +140,7 @@ class UserConfigForm extends Model {
         try {
             $userId = \Yii::$app->user->id;
             if (!UserInfo::updateAll(['ALLOW_RECONSUME_SMS' => $this->allowReconsumeSms?$this->allowReconsumeSms:0], 'USER_ID=:USER_ID', [':USER_ID' => $userId])) {
-                throw new Exception('更新个人设置失败');
+                throw new Exception(Yii::t('app', 'updateUserConfigFailed'));
             }
             //开启扣费
             $smsTo = UserInfo::findOneAsArray('USER_ID=:USER_ID', [':USER_ID' => $userId], 'ALLOW_RECONSUME_SMS_TO');
@@ -160,11 +161,11 @@ class UserConfigForm extends Model {
                         if (Balance::getAvailableBalance($userId) < $smsFee) continue;
                         Balance::changeUserBonus($userId, 'bonus', -abs($smsFee), ['DEAL_TYPE_ID' => DealType::SMS, 'REMARK' => $year . '年复销提醒短信服务费']);
                         if (!UserInfo::updateAll(['ALLOW_RECONSUME_SMS_TO' => $to], 'USER_ID=:USER_ID', [':USER_ID' => $userId])) {
-                            throw new Exception('更新个人短信有效期失败');
+                            throw new Exception(Yii::t('app', 'updateUserSMSExpiredFailed'));
                         }
                         break;
                     } elseif ($item == 2) {
-                        throw new Exception('不存在此方式');
+                        throw new Exception(Yii::t('app', 'modeDoesNotExist'));
                         break;
                     }
                 }

+ 5 - 4
common/models/forms/UserForm.php

@@ -15,6 +15,7 @@ use common\models\User;
 use common\models\UserInfo;
 use common\models\UserNetwork;
 use common\models\UserRelation;
+use Yii;
 use yii\db\Exception;
 
 /**
@@ -576,7 +577,7 @@ class UserForm extends Model
         $uid = \Yii::$app->user->id;
         $model = User::findOne(['ID'=>$uid]);
         if ( !$model->validatePassword($this->oldPassword) ) {
-            $this->addError('modifyPassword', 'Original login password error');//原登录密码错误
+            $this->addError('modifyPassword', Yii::t('app', 'originalLoginPasswordError'));
             return false;
         }
         $model->PASSWORD_HASH = \Yii::$app->security->generatePasswordHash($this->password);
@@ -596,7 +597,7 @@ class UserForm extends Model
         $uid = \Yii::$app->user->id;
         $model = User::findOne(['ID'=>$uid]);
         if ( !$model->validatePasswordPay($this->oldPassword) ) {
-            $this->addError('modifyPasswordPay', 'Original payment password error');//原支付密码错误
+            $this->addError('modifyPasswordPay', Yii::t('app', 'originalPaymentPasswordError'));
             return false;
         }
         $model->PAY_PASSWORD = \Yii::$app->security->generatePasswordHash($this->payPassword);
@@ -617,11 +618,11 @@ class UserForm extends Model
         }
         $model =  User::findOne(["USER_NAME"=>$this->userName]);
         if( !$model ) {
-            $this->addError('noLoginModifyPassword', 'Nonexistent user name');//不存在的用户名
+            $this->addError('noLoginModifyPassword', Yii::t('app', 'userNameNotExists'));
             return false;
         }
         if ( !$model->validatePassword($this->oldPassword) ) {
-            $this->addError('noLoginModifyPassword', 'Original login password error');//原登录密码错误
+            $this->addError('noLoginModifyPassword', Yii::t('app', 'originalLoginPasswordError'));
             return false;
         }
         $model->PASSWORD_HASH = \Yii::$app->security->generatePasswordHash($this->password);

+ 3 - 2
common/models/forms/WithdrawForm.php

@@ -27,6 +27,7 @@ use common\models\UserTeamwork;
 use common\models\Withdraw;
 use common\models\WithdrawLevel;
 use common\helpers\http\LingYunGongApi;
+use Yii;
 use yii\base\Exception;
 
 /**
@@ -492,10 +493,10 @@ class WithdrawForm extends Model {
         $transaction = $db->beginTransaction();
         try {
             if (Cache::getSystemConfig()['isCanWithdrawBack']['VALUE'] != 1) {
-                throw new Exception('提现退回已关闭');
+                throw new Exception(Yii::t('app', 'isCanWithdrawBack'));
             }
             if (!Withdraw::find()->where('USER_ID=:USER_ID AND ID=:ID AND AUDIT_STATUS=:AUDIT_STATUS_1', ['USER_ID' => \Yii::$app->user->id, ':ID' => $this->id, ':AUDIT_STATUS_1' => Withdraw::STATUS_APPLIED])->exists()) {
-                throw new Exception('符合退回的提现记录不存在');
+                throw new Exception(Yii::t('app', 'withdrawRecordDoesNotExist'));
             }
             $oneWithdraw->BACK_REMARK = $this->createRemark;
             $oneWithdraw->BACK_FAIL_AT = Date::nowTime();

+ 9 - 1
console/controllers/CalcController.php

@@ -16,11 +16,12 @@ use common\helpers\bonus\Calc\PushBaseDataToCalc;
 use common\helpers\Cache;
 use common\models\CalcRecord;
 use common\models\Period;
+use common\models\CalcOperation;
 
 
 class CalcController extends BaseController
 {
-
+    const  CALC_DB_NAME = 'dbCalcServer';
     public function actionPerfOrder($taskKey)
     {
         $params    = Cache::getAsyncParams($taskKey);
@@ -194,16 +195,23 @@ class CalcController extends BaseController
      */
     public function actionPullBonus($taskKey): bool
     {
+        $db        = self::CALC_DB_NAME;
         $params    = Cache::getAsyncParams($taskKey);
         $periodNum = $params['periodNum'] ?? 0;
         CalcRecord::record($periodNum, '【奖金计算】开始拉取'.$periodNum.'期奖金数据');
+        $currentPeriod = Period::find()->where(['PERIOD_NUM' => $periodNum])
+            ->asArray()->one();
         //推送数据 状态已修改
         $res = (new PullCalcBonusData($periodNum))->start();
         if (200 == $res['code']) {
             self::recordCalcAndProcessStatus($periodNum, '【奖金计算】'.$periodNum.'期奖金数据已全部拉取');
+            \Yii::$app->$db->createCommand()->update('AR_PERIOD', ['CALC_ID' => ''], 'PERIOD_NUM=:PERIOD_NUM', ['PERIOD_NUM' => $periodNum])->execute();
+            CalcOperation::closeOperation($currentPeriod['CALC_ID']);
             return true;
         } else {
             self::recordCalcAndProcessStatus($periodNum, '【奖金计算】'.$periodNum.'期奖金数据拉取失败,原因:' . $res['msg'] ?? '');
+            \Yii::$app->$db->createCommand()->update('AR_PERIOD', ['CALC_ID' => ''], 'PERIOD_NUM=:PERIOD_NUM', ['PERIOD_NUM' => $periodNum])->execute();
+            CalcOperation::closeOperation($currentPeriod['CALC_ID']);
             return false;
         }
     }

+ 1 - 1
frontendApi/config/menu.php

@@ -84,7 +84,7 @@ return [
             ['name'=>'Team inquiry', 'class'=>'', 'icon'=>'', 'controller'=>'bonus', 'action'=>'teams', 'routePath'=>'bonus/teams', 'show'=>1,], // 团队查询
             ['name'=>'Historical cumulative bonus', 'class'=>'', 'icon'=>'', 'controller'=>'bonus', 'action'=>'other', 'routePath'=>'bonus/historical-cumulative-bonus', 'show'=>1,'allow'=>'pastBonusSwitch'],//历史累积奖金
             //['name'=>'实时业绩', 'class'=>'', 'icon'=>'', 'controller'=>'bonus', 'action'=>'real-time-perf', 'routePath'=>'bonus/real-time-perf', 'show'=>1,],
-           // ['name'=>'房产积分', 'class'=>'', 'icon'=>'', 'controller'=>'bonus', 'action'=>'fc-point', 'routePath'=>'bonus/fc-point', 'show'=>1,],
+            // ['name'=>'房产积分', 'class'=>'', 'icon'=>'', 'controller'=>'bonus', 'action'=>'fc-point', 'routePath'=>'bonus/fc-point', 'show'=>1,],
 //            ['name'=>'报单中心补助明细查询', 'class'=>'', 'icon'=>'', 'controller'=>'bonus', 'action'=>'flow-bt', 'routePath'=>'bonus/flow-bt', 'show'=>1,],
 //            ['name'=>'报单中心货补追溯', 'class'=>'', 'icon'=>'', 'controller'=>'bonus', 'action'=>'trace-fl', 'routePath'=>'bonus/trace-fl', 'show'=>1,],
             ['name'=>'Withdrawal details', 'class'=>'', 'icon'=>'', 'controller'=>'finance', 'action'=>'withdraw', 'routePath'=>'finance/withdraw', 'show'=>0,],//提现明细

+ 116 - 0
frontendApi/config/menuV2.php

@@ -0,0 +1,116 @@
+<?php
+return [
+    'dashboard'=>[
+        'name'=>'Dashboard',//控制台
+        'class' => '',
+        'icon'=>'el-icon-guide',
+        'controller'=>'dashboard',
+        'action'=>'index',
+        'routePath'=>'dashboard/index',
+        'show'=>1,
+        'wiki' => 'dashboard',
+        'child'=>[
+            ['name'=>'Month Bonus', 'class'=>'', 'icon'=>'', 'controller'=>'dashboard', 'action'=>'bonus-num', 'routePath'=>'dashboard/bonus-num', 'show'=>0, 'wiki' => 'monthBonus',], // 月奖
+        ]
+    ],
+    'shop'=>[
+        'name'=>'Shopping Mall',//商城管理
+        'class' => '',
+        'icon'=>'el-icon-goods',
+        'controller'=>'shop',
+        'action'=>'',
+        'routePath'=>'shop',
+        'show'=>1,
+        'wiki' => 'shoppingMall',
+        'child'=>[
+            ['name'=>'Standard Products', 'class'=>'', 'icon'=>'', 'controller'=>'shop', 'action'=>'standard-products', 'routePath'=>'shop/standard-products', 'show'=>1, 'wiki' => 'standardProducts',],//普通商品
+            ['name'=>'Car Fund Products', 'class'=>'', 'icon'=>'', 'controller'=>'shop', 'action'=>'car-fund-products', 'routePath'=>'shop/car-fund-products', 'show'=>1, 'wiki' => 'carFundProducts',],//车奖商品
+            ['name'=>'Villa Fund Products', 'class'=>'', 'icon'=>'', 'controller'=>'shop', 'action'=>'villa-fund-products', 'routePath'=>'shop/villa-fund-products', 'show'=>1, 'wiki' => 'villaFundProducts',],//房奖商品
+            ['name'=>'Welcome Pack', 'class'=>'', 'icon'=>'', 'controller'=>'user', 'action'=>'dec', 'routePath'=>'user/dec', 'show'=>1,'allow'=>'declarer', 'wiki' => 'welcomePack',], // 报单管理
+            ['name'=>'Welcome Pack Listing', 'class'=>'', 'icon'=>'', 'controller'=>'shop', 'action'=>'dec-order-list', 'routePath'=>'shop/dec-order-list', 'show'=>1, 'wiki' => 'welcomePackList',],//报单列表
+            ['name'=>'Upgrade Management', 'class'=>'', 'icon'=>'', 'controller'=>'upgrade', 'action'=>'dec', 'routePath'=>'user/upgrade', 'show'=>1,'allow'=>'declarer', 'wiki' => 'memberUpgrade',],//升级管理
+            ['name'=>'Order List', 'class'=>'', 'icon'=>'', 'controller'=>'shop', 'action'=>'order-list', 'routePath'=>'shop/order-list', 'show'=>1, 'wiki' => 'orderList',],//订单列表
+            ['name'=>'Brand Ambassador', 'class'=>'', 'icon'=>'', 'controller'=>'user', 'action'=>'ba-dec', 'routePath'=>'user/ba-dec', 'show'=>1,'allow'=>'declarer', 'wiki' => 'brandAmbassador',],// BA会员
+            ['name'=>'BA Order List', 'class'=>'', 'icon'=>'', 'controller'=>'shop', 'action'=>'ba-dec-order-list', 'routePath'=>'shop/ba-dec-order-list', 'show'=>1,'allow'=>'declarer', 'wiki' => 'BAOrderList',],// BA订单
+        ]
+    ],
+    'user'=>[
+        'name'=>'Member Management',//会员管理
+        'class' => '',
+        'icon'=>'el-icon-user',
+        'controller'=>'user',
+        'action'=>'',
+        'routePath'=>'user',
+        'show'=>0,
+        'wiki' => 'memberManagement',
+        'child'=>[
+            ['name'=>'Personal Info', 'class'=>'', 'icon'=>'', 'controller'=>'user', 'action'=>'index', 'routePath'=>'user/index', 'show'=>0, 'wiki' => 'personalInfo',],//个人资料
+            ['name'=>'Reset Password', 'class'=>'', 'icon'=>'', 'controller'=>'user', 'action'=>'password', 'routePath'=>'user/password', 'show'=>0, 'wiki' => 'resetPassword',],//重设密码
+        ]
+    ],
+    'atlas'=>[
+        'name'=>'Network Chart',
+        'class' => '',
+        'icon'=>'el-icon-data-line',
+        'controller'=>'atlas',
+        'action'=>'',
+        'routePath'=>'atlas',
+        'show'=>1,
+        'wiki' => 'networkChart',
+        'child'=>[
+            ['name'=>'Placement Network', 'class'=>'', 'icon'=>'', 'controller'=>'atlas', 'action'=>'network', 'routePath'=>'atlas/network', 'show'=>1, 'wiki' => 'placementNetwork',],//安置网络
+            ['name'=>'Sponsor Network', 'class'=>'', 'icon'=>'', 'controller'=>'atlas', 'action'=>'relation', 'routePath'=>'atlas/relation', 'show'=>1, 'wiki' => 'sponsorNetwork',],//推荐网路
+            ['name'=>'Brand Ambassador List', 'class'=>'', 'icon'=>'', 'controller'=>'atlas', 'action'=>'brand-ambassador-list', 'routePath'=>'atlas/brand-ambassador-list', 'show'=>1, 'wiki' => 'brandAmbassadorList',],// BA会员
+        ]
+    ],
+    'bonus'=>[
+        'name'=>'Bonus Management',//奖金管理
+        'class' => '',
+        'icon'=>'el-icon-coin',
+        'controller'=>'bonus',
+        'action'=>'',
+        'routePath'=>'bonus',
+        'show'=>1,
+        'wiki' => 'bonusManagement',
+        'child'=>[
+            ['name'=>'My Account', 'class'=>'', 'icon'=>'', 'controller'=>'bonus', 'action'=>'index', 'routePath'=>'bonus/index', 'show'=>1, 'wiki' => 'myAccount',],//我的账户
+            ['name'=>'My Bonus', 'class'=>'', 'icon'=>'', 'controller'=>'bonus', 'action'=>'other', 'routePath'=>'bonus/other', 'show'=>1,'allow'=>'pastBonusSwitch', 'wiki' => 'myBonus'],//我的奖金
+            ['name'=>'Team Inquiry', 'class'=>'', 'icon'=>'', 'controller'=>'bonus', 'action'=>'teams', 'routePath'=>'bonus/teams', 'show'=>1, 'wiki' => 'teamInquiry'], // 团队查询
+            ['name'=>'Historical Cumulative Bonus', 'class'=>'', 'icon'=>'', 'controller'=>'bonus', 'action'=>'other', 'routePath'=>'bonus/historical-cumulative-bonus', 'show'=>1,'allow'=>'pastBonusSwitch', 'wiki' => 'historicalCumulativeBonus'],//历史累积奖金
+            ['name'=>'Withdrawal Details', 'class'=>'', 'icon'=>'', 'controller'=>'finance', 'action'=>'withdraw', 'routePath'=>'finance/withdraw', 'show'=>0, 'wiki' => 'withdrawalDetails'],//提现明细
+            ['name'=>'Apply For Withdraw', 'class'=>'', 'icon'=>'', 'controller'=>'finance', 'action'=>'withdraw-add', 'routePath'=>'finance/withdraw-add', 'show'=>0, 'wiki' => 'applyWithdraw'],// 申请提现
+            ['name'=>'Recharge Details', 'class'=>'', 'icon'=>'', 'controller'=>'finance', 'action'=>'recharge', 'routePath'=>'finance/recharge', 'show'=>1, 'wiki' => 'rechargeDetails'],//充值明细
+            ['name'=>'Apply For Recharge', 'class'=>'', 'icon'=>'', 'controller'=>'finance', 'action'=>'recharge-add', 'routePath'=>'finance/recharge-add', 'show'=>0, 'wiki' => 'applyRecharge'], // 申请充值
+            ['name'=>'Transfer Details', 'class'=>'', 'icon'=>'', 'controller'=>'finance', 'action'=>'transfer-list', 'routePath'=>'finance/transfer-list', 'show'=>1,'allow'=>'transferRecordSwitch', 'wiki' => 'transferDetails'],//转账明细
+            ['name'=>'Apply For Transfer', 'class'=>'', 'icon'=>'', 'controller'=>'finance', 'action'=>'transfer-add', 'routePath'=>'finance/transfer-add', 'show'=>0,'allow'=>'transferSwitch', 'wiki' => 'applyTransfer'], // 申请转账
+        ]
+    ],
+
+    'article'=>[
+        'name'=>'Article Management',//文章管理
+        'class' => '',
+        'icon'=>'el-icon-document',
+        'controller'=>'article',
+        'action'=>'',
+        'routePath'=>'article',
+        'show'=>1,
+        'wiki' => 'articleManagement',
+        'child'=>[
+            ['name'=>'System Notification', 'class'=>'', 'icon'=>'', 'controller'=>'article', 'action'=>'article-list', 'routePath'=>'article/article-list', 'show'=>1, 'wiki' => 'systemNotification'],//系统通知
+        ]
+    ],
+
+    'config'=>[
+        'name'=>'Setting',//设置
+        'class' => '',
+        'icon'=>'el-icon-setting',
+        'controller'=>'config',
+        'action'=>'',
+        'routePath'=>'config',
+        'show'=>1,
+        'wiki' => 'setting',
+        'child'=>[
+            ['name'=>'Shipping Address', 'class'=>'', 'icon'=>'', 'controller'=>'config', 'action'=>'receive-address-list', 'routePath'=>'config/receive-address-list', 'show'=>1, 'wiki' => 'shippingAddress'],//收货地址
+        ]
+    ],
+];

+ 1 - 0
frontendApi/config/params.php

@@ -16,6 +16,7 @@ return [
         'v1/site/doc',
         'v1/site/config',
         'v1/shop/verify-approach-order',
+        'v1/finance/prove-add',
         ],
     'noCheckPermissionActions' => [],
 ];

+ 10 - 4
frontendApi/modules/v1/controllers/ArticleController.php

@@ -9,6 +9,7 @@ namespace frontendApi\modules\v1\controllers;
 
 use common\models\Article;
 use common\models\ArticleCategory;
+use Yii;
 
 class ArticleController extends BaseController
 {
@@ -53,17 +54,22 @@ class ArticleController extends BaseController
      */
     public function actionList(){
         $cid = \Yii::$app->request->get('cid');
-        $condition = ' AND STATUS=1';
+        $condition = ' AND A.STATUS=1';
         $params = [];
         if($cid){
             $condition .= ' AND CID=:CID';
             $params[':CID'] = $cid;
         }
         $data = Article::lists($condition, $params, [
-            'select' => 'ID,TITLE,CID,CREATED_AT',
-            'orderBy' => 'CREATED_AT DESC',
+            'select' => 'A.ID,A.TITLE,A.CID,A.CREATED_AT,C.CATE_NAME',
+            'from' => Article::tableName().' AS A',
+            'join' => [
+                ['LEFT JOIN', ArticleCategory::tableName() . ' AS C', 'A.CID = C.ID'],
+            ],
+            'orderBy' => 'A.CREATED_AT DESC',
             'useSlaves' => true,
         ]);
+
         // 全部分类
         $data['allCategory'] = ArticleCategory::getAllCategory();
         return static::notice($data);
@@ -90,7 +96,7 @@ class ArticleController extends BaseController
             }
             return static::notice($data);
         } else {
-            return static::notice('The article does not exist', 400);// 文章不存在
+            return static::notice(Yii::t('app', 'articleDoesNotExist'), 400);// 文章不存在
         }
     }
 }

+ 20 - 15
frontendApi/modules/v1/controllers/AtlasController.php

@@ -21,6 +21,8 @@ use common\models\UserNetwork;
 use common\models\UserRelation;
 use Yii;
 use common\models\User;
+use yii\data\Pagination;
+use yii\db\Query;
 
 class AtlasController extends BaseController {
     public $modelClass = User::class;
@@ -43,13 +45,13 @@ class AtlasController extends BaseController {
 
         if ($userName !== '') {
             if (!$userId = Info::getUserIdByUserName($userName)) {
-                return static::notice('Member does not exist', 400); // 会员不存在
+                return static::notice(Yii::t('app', 'memberDoesNotExist'), 400);
             }
             $topUid = \Yii::$app->user->id;
             $userNetInfo = UserNetwork::findOneAsArray('USER_ID=:USER_ID', [':USER_ID' => $userId], 'PARENT_UIDS');
             $parentUidsArr = explode(',', $userNetInfo['PARENT_UIDS']);
             if(!in_array($topUid,$parentUidsArr)){
-                return static::notice('The member is not in the same placement network as the current user', 400); // 会员与当前用户不再同一安置网络内
+                return static::notice(Yii::t('app', 'memberDoesNotInSamePlacementNetwork'), 400);
             }
 
         } else {
@@ -91,7 +93,7 @@ class AtlasController extends BaseController {
         $periodNum = Yii::$app->request->get('periodNum', null);
         $deep = Yii::$app->request->get('deep', 5);
         if ($deep > 20) {
-            return static::notice('View the top 20 sub members of the member at most', 400); // 最多查看会员的前20层子会员
+            return static::notice(Yii::t('app', 'viewSubMembersMost'), 400);
         }
         $allData = UserNetwork::getChildrenWithDeepAndLayer($userId, $deep, 1, $periodNum);
         return static::notice(['allData' => $allData, 'periodNum' => $periodNum]);
@@ -112,13 +114,13 @@ class AtlasController extends BaseController {
             $userId = \Yii::$app->user->id;
         } else {
             if (!$userId = Info::getUserIdByUserName($userName)) {
-                return static::notice('Member does not exist', 400); //  会员不存在
+                return static::notice(Yii::t('app', 'memberDoesNotExist'), 400);
             }
             $topUid = \Yii::$app->user->id;
             $userNetInfo = UserNetwork::findOneAsArray('USER_ID=:USER_ID', [':USER_ID' => $userId], 'PARENT_UIDS');
             $parentUidsArr = explode(',', $userNetInfo['PARENT_UIDS']);
             if(!in_array($topUid,$parentUidsArr)){
-                return static::notice('The member is not in the same placement network as the current user', 400); // 会员与当前用户不再同一安置网络内
+                return static::notice(Yii::t('app', 'memberDoesNotInSamePlacementNetwork'), 400);
             }
         }
         $period = Period::instance();
@@ -166,7 +168,7 @@ class AtlasController extends BaseController {
         $periodNum = Yii::$app->request->get('periodNum', null);
         $deep = Yii::$app->request->get('deep', 5);
         if ($deep > 20) {
-            return static::notice('View the top 20 sub members of the member at most', 400); // 最多查看会员的前20层子会员
+            return static::notice(Yii::t('app', 'viewSubMembersMost'), 400);
         }
         $allData = UserRelation::getChildrenWithDeepAndLayer($userId, $deep, 1, $periodNum);
         return static::notice(['allData' => $allData, 'periodNum' => $periodNum]);
@@ -187,13 +189,13 @@ class AtlasController extends BaseController {
             $userId = \Yii::$app->user->id;
         } else {
             if (!$userId = Info::getUserIdByUserName($userName)) {
-                return static::notice('Member does not exist', 400); //  会员不存在
+                return static::notice(Yii::t('app', 'memberDoesNotExist'), 400);
             }
             $topUid = \Yii::$app->user->id;
             $userNetInfo = UserRelation::findOneAsArray('USER_ID=:USER_ID', [':USER_ID' => $userId], 'PARENT_UIDS');
             $parentUidsArr = explode(',', $userNetInfo['PARENT_UIDS']);
             if(!in_array($topUid,$parentUidsArr)){
-                return static::notice('会员与当前用户不再同一安置网络内', 400);
+                return static::notice(Yii::t('app', 'memberDoesNotInSamePlacementNetwork'), 400);
             }
         }
         $period = Period::instance();
@@ -225,13 +227,16 @@ class AtlasController extends BaseController {
      */
     public function actionBrandAmbassadorList()
     {
-        $data = BaUserInfo::find()->alias('I')
-            ->select('I.USER_ID, I.USER_NAME, U.REAL_NAME, U.PERIOD_AT, U.WHETHER_UPGRADE, U.BA_UPGRADE_AT')
-            ->where('I.REC_UID = :REC_UID', ['REC_UID' => \Yii::$app->user->id])
-            ->join('INNER JOIN', BaUser::tableName() . ' AS U', 'I.USER_ID = U.ID')
-            ->orderBy('I.CREATED_AT DESC')
-            ->asArray()
-            ->all();
+        $condition = ' AND I.REC_UID = :REC_UID ';
+        $params[':REC_UID'] = \Yii::$app->user->id;
+        $data = BaUserInfo::lists($condition, $params, [
+            'select' => 'I.USER_ID, I.USER_NAME, U.REAL_NAME, U.PERIOD_AT, U.WHETHER_UPGRADE, U.BA_UPGRADE_AT',
+            'from' => BaUserInfo::tableName().' AS I',
+            'join' => [
+                ['INNER JOIN', BaUser::tableName().' AS U', 'I.USER_ID = U.ID'],
+            ],
+            'orderBy' => 'I.CREATED_AT DESC',
+        ]);
 
         return static::notice($data);
     }

+ 22 - 20
frontendApi/modules/v1/controllers/BaController.php

@@ -12,6 +12,7 @@ use common\helpers\Cache;
 use common\helpers\Form;
 use common\helpers\Tool;
 use common\helpers\user\Info;
+use common\models\BaUserInfo;
 use common\models\Config;
 use common\models\DeclarationLevel;
 use common\models\DeclarationPackage;
@@ -33,6 +34,7 @@ use common\models\UserBind;
 use common\models\UserInfo;
 use common\models\UserNetwork;
 use common\models\forms\DeclarationUpgradeForm;
+use Yii;
 use yii\db\Exception;
 use yii\web\UploadedFile;
 
@@ -65,12 +67,12 @@ class BaController extends BaseController {
             $post = \Yii::$app->request->post();
             $form->scenario = 'modifyProfile';
             if($form->load($post, '') && $result = $form->modifyProfile()){
-                return static::notice('Personal data modified successfully');//个人资料修改成功
+                return static::notice(Yii::t('app', 'personalDataModifiedSuccessfully'));
             } else {
                 return static::notice(Form::formatErrorsForApi($form->getErrors()), 400);
             }
         }
-        return static::notice('Illegal request', 400); // 非法请求
+        return static::notice(Yii::t('app', 'illegalRequest'), 400);
     }
 
     /**
@@ -82,12 +84,12 @@ class BaController extends BaseController {
             $form->scenario = 'modifyPassword';
             $post = \Yii::$app->request->post();
             if($form->load($post, '') && $result = $form->modifyPassword()){
-                return static::notice('Password modified successfully');//密码修改成功
+                return static::notice(Yii::t('app', 'passwordModifiedSuccessfully'));
             } else {
                 return static::notice(Form::formatErrorsForApi($form->getErrors()), 400);
             }
         }
-        return static::notice('Illegal request', 400); // 非法请求
+        return static::notice(Yii::t('app', 'illegalRequest'), 400);
     }
 
     /**
@@ -100,12 +102,12 @@ class BaController extends BaseController {
             $post = \Yii::$app->request->post();
             $form->userId = \Yii::$app->user->id;
             if($form->load($post, '') && $result = $form->modifyPasswordPay()){
-                return static::notice('支付密码修改成功');
+                return static::notice(Yii::t('app', 'passwordModifiedSuccessfully'));
             } else {
                 return static::notice(Form::formatErrorsForApi($form->getErrors()), 400);
             }
         }
-        return static::notice('Illegal request', 400); // 非法请求
+        return static::notice(Yii::t('app', 'illegalRequest'), 400);
     }
 
     /**
@@ -137,7 +139,7 @@ class BaController extends BaseController {
             //$formModel->token = \Yii::$app->request->post('uploadToken');
             $formModel->token = \Yii::$app->request->request('uploadToken');
             if ($formModel->file && $formModel->upload()) {
-                return static::notice('Successful');
+                return static::notice(Yii::t('app', 'successfully'));
             } else {
                 return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
             }
@@ -188,7 +190,7 @@ class BaController extends BaseController {
     public function actionBindEdit(){
         $id = \Yii::$app->request->get('id');
         if(\Yii::$app->request->isPost) {
-            return parent::edit(UserBindForm::class, '修改主点位成功', 'frontEdit', ['frontEdit'], null, function($form, $result){
+            return parent::edit(UserBindForm::class, Yii::t('app', 'successfully'), 'frontEdit', ['frontEdit'], null, function($form, $result){
                 //log
             });
         }
@@ -216,12 +218,12 @@ class BaController extends BaseController {
         ->one();
         $isOpen = !empty($isSwitchUpgrade) && isset($isSwitchUpgrade['VALUE']) ? $isSwitchUpgrade['VALUE'] : 0;
         if ($isOpen < 1) {
-            return static::notice('The function is not available',400); // 功能暂未开放
+            return static::notice(Yii::t('app', 'theFunctionIsNotAvailable'), 400);
         }
         $userNumber = \Yii::$app->request->request('userName');
         $baseInfo = Info::baseInfoZhByUserName($userNumber);
         if ($baseInfo['STATUS'] != 1) {
-            return static::notice('Inactive user. Please contact customer service.',400);// 非激活用户,请联系客服
+            return static::notice(Yii::t('app', 'inactiveUser'), 400);
         }
         // 1. 如果是最高级别,则只显示用户基本信息
         // 2. 如果不是最高级别,如果用户累计报单数据是0, 或者用户累计报单业绩不符合级别信息,则提示 请联系客服核对业绩
@@ -266,10 +268,10 @@ class BaController extends BaseController {
             $nextLevelPerf = DeclarationLevel::getNextDecPref($levelPerf)['PERF'];
             // 如果总和超过了下一级业绩
             if ($userDecPvSum >= $nextLevelPerf) {
-                return static::notice('Please contact customer service to check performance.',400);// 请联系客服人员核对业绩
+                return static::notice(Yii::t('app', 'checkPerformance'), 400);
             }
             $type = $isObserve ? 1 : 2;
-            $userInfo['UPGRADE_FUNC'] = $isObserve ? 'filling up of a deficit' : 'full payment';// 升级方式
+            $userInfo['UPGRADE_FUNC'] = $isObserve ? Yii::t('app', 'fillingUpOfADeficit') : Yii::t('app', 'fullPayment');// 升级方式
             $upgradeType = UpgradeType::getOneByType($type);
             // 如果用户不是最大级别,则需要获取是否观察期,算出PV是否有问题,应该补多少,
             $userInfo['UPGRADE_TYPE'] = $upgradeType;
@@ -294,7 +296,7 @@ class BaController extends BaseController {
         ->one();
         $isOpen = !empty($isSwitchUpgrade) && isset($isSwitchUpgrade['VALUE']) ? $isSwitchUpgrade['VALUE'] : 0;
         if ($isOpen < 1) {
-            return static::notice('功能暂未开放',400);
+            return static::notice(Yii::t('app', 'theFunctionIsNotAvailable'), 400);
         }
         // 开始升级
         if (\Yii::$app->request->isPost) {
@@ -386,10 +388,10 @@ class BaController extends BaseController {
             $insertUserName = strtoupper($post['insertUserName']);
             $getRedisUserName = $redis->get('key_' . $insertUserName);
             if (!$getRedisUserName){
-                return static::notice('Membership number expired',400);//会员编号过期
+                return static::notice(Yii::t('app', 'memberNumberExpired'), 400);
             }
             if ($insertUserName != $getRedisUserName){
-                return static::notice('Member number does not conform to',400);//会员编号不符合
+                return static::notice(Yii::t('app', 'memberNumberDoesNotConformTo'), 400);
             }
 
             $post['insertUserName'] = $insertUserName;
@@ -428,7 +430,7 @@ class BaController extends BaseController {
         // 所有开户行
         $allOpenBank = OpenBank::find()->where('STATUS=1')->orderBy('LIST_ORDER ASC')->asArray()->all();
         if (!$userName) {
-            return static::notice('Failed to generate member number', 400);//会员编号生成失败
+            return static::notice(Yii::t('app', 'failedToGenerateMemberNumber'), 400);
         }
         //随机码保存在redis中方便进行比对
         $redis->setex('key_'.$userName , 3600 , $userName);
@@ -496,7 +498,7 @@ class BaController extends BaseController {
             $userInfo['isLocation'] = '('.implode(',',$isLocation).')';
             return static::notice($userInfo);
         }else{
-            return static::notice('Member number does not exist', 400);//会员编号不存在
+            return static::notice(Yii::t('app', 'memberNumberDoesNotExist'), 400);
         }
     }
 
@@ -533,7 +535,7 @@ class BaController extends BaseController {
             ];
             return static::notice($arr);
         }else{
-            return static::notice('Repeat sales Member No. does not exist', 400);//复消会员编号不存在
+            return static::notice(Yii::t('app', 'repeatSalesMemberNoDoesNotExist'), 400);
         }
     }
 
@@ -566,12 +568,12 @@ class BaController extends BaseController {
             $post = \Yii::$app->request->post();
             $form->scenario = 'modifyProfile';
             if ($form->load($post, '') && $result = $form->modifyProfile()){
-                return static::notice('Personal data modified successfully');//个人资料修改成功
+                return static::notice(Yii::t('app', 'personalDataModifiedSuccessfully'), 400);
             } else {
                 return static::notice(Form::formatErrorsForApi($form->getErrors()), 400);
             }
         }
 
-        return static::notice('Illegal request', 400); // 非法请求
+        return static::notice(Yii::t('app', 'illegalRequest'), 400);
     }
 }

+ 15 - 7
frontendApi/modules/v1/controllers/BaseController.php

@@ -12,6 +12,7 @@ use common\components\ActiveRecord;
 use common\helpers\Date;
 use common\helpers\Form;
 use common\helpers\LoggerTool;
+use common\helpers\Tool;
 use \frontendApi\modules\v1\models\brand\User AS Brand;
 use frontendApi\modules\v1\models\User;
 use Yii;
@@ -36,7 +37,7 @@ class BaseController extends \yii\rest\ActiveController {
         $isQuickly = User::isQuicklyLogin();
         $requestMethod = Yii::$app->request->getMethod();
         if ($isQuickly == 1 && strtoupper($requestMethod) != 'GET') {
-            throw new ForbiddenHttpException('快速登录的会员无法进行任何操作!');
+            throw new ForbiddenHttpException(Yii::t('app', 'quickLoginCanNotOperate'));
         }
     }
 
@@ -47,7 +48,7 @@ class BaseController extends \yii\rest\ActiveController {
         $isQuickly = Brand::isQuicklyLogin();
         $requestMethod = Yii::$app->request->getMethod();
         if ($isQuickly == 1 && strtoupper($requestMethod) != 'GET') {
-            throw new ForbiddenHttpException('快速登录的会员无法进行任何操作!');
+            throw new ForbiddenHttpException(Yii::t('app', 'quickLoginCanNotOperate'));
         }
     }
 
@@ -77,7 +78,7 @@ class BaseController extends \yii\rest\ActiveController {
                 $currentTime = time();
                 $timeOut = Yii::$app->params['operationTimeOut'];
                 if ($currentTime - $lastTime > $timeOut) {
-                    return self::notice('Connection not operated for too long', 402);
+                    return self::notice(Yii::t('app', 'notConnection'), 402);
                 } else {
                     Yii::$app->tokenRedis->hset($redisKey, $userId, time());
                 }
@@ -101,13 +102,20 @@ class BaseController extends \yii\rest\ActiveController {
                 $currentTime = time();
                 $timeOut = Yii::$app->params['operationTimeOut'];
                 if ($currentTime - $lastTime > $timeOut) {
-                    return self::notice('Connection not operated for too long', 402);
+                    return self::notice(Yii::t('app', 'notConnection'), 402);
                 } else {
                     Yii::$app->tokenRedis->hset($redisKey, $userId, time());
                 }
             }
         }
 
+        // 动态返回语言:zh:zh-CN | en:en-US
+        if (!Yii::$app->request->isOptions) {
+            $language = Yii::$app->request->headers->get('language') ?? 'en';
+            Yii::$app->language = $language == 'zh' ? 'zh-CN' : 'en-US';
+            Yii::$app->sourceLanguage = $language == 'zh' ? 'en-US' : 'zh-CN';
+        }
+
         return parent::beforeAction($action);
     }
 
@@ -187,7 +195,7 @@ class BaseController extends \yii\rest\ActiveController {
             $selected = \Yii::$app->request->post('selected');
         }
         if (!$selected) {
-            return self::notice('必须选择一条删除数据', 500);
+            return self::notice(Yii::t('app', 'selectAtLeastOne'), 500);
         }
 
         // 是否存在 DONT_DEL 字段
@@ -223,7 +231,7 @@ class BaseController extends \yii\rest\ActiveController {
             if ($isDelData) {
                 // 真实删除数据
                 if (!$modelClass::deleteAll($condition, $params)) {
-                    throw new Exception('failed to delete');//删除失败
+                    throw new Exception(Yii::t('app', 'deleteFailed'));
                 }
             } else {
                 // 设置IS_DEL字段为1
@@ -231,7 +239,7 @@ class BaseController extends \yii\rest\ActiveController {
             }
             if ($afterFun) $afterFun($selected);
             $transaction->commit();
-            return self::notice('delete successfully');//删除成功
+            return self::notice(Yii::t('app', 'deleteSuccessfully'));
         } catch (Exception $e) {
             $transaction->rollBack();
             return self::notice($e->getMessage(), 500);

+ 62 - 44
frontendApi/modules/v1/controllers/BonusController.php

@@ -12,7 +12,9 @@ namespace frontendApi\modules\v1\controllers;
 
 use common\helpers\Cache;
 use common\helpers\Date;
+use common\helpers\Form;
 use common\helpers\Tool;
+use common\helpers\user\Balance;
 use common\helpers\user\Info;
 use common\helpers\user\Perf;
 use common\models\CalcBonus;
@@ -28,17 +30,28 @@ use common\models\FlowReconsumePoints;
 use common\models\FlowTourismPoints;
 use common\models\FlowVillaPoints;
 use common\models\FlowWallet;
+use common\models\forms\TransferForm;
+use common\models\forms\UploadForm;
+use common\models\forms\WithdrawForm;
+use common\models\InvoiceAudit;
 use common\models\PerfMonth;
 use common\models\PerfPeriod;
 use common\models\Period;
 use common\models\DecRole;
 use common\models\EmployLevel;
 use common\models\FlowExchangePoints;
+use common\models\RegType;
 use common\models\ScoreMonth;
+use common\models\Transfer;
+use common\models\Uploads;
 use common\models\UserBonus;
+use common\models\UserInfo;
 use common\models\UserWallet;
 use common\models\UserRelation;
+use common\models\Withdraw;
 use frontendApi\modules\v1\models\User;
+use Yii;
+use yii\web\UploadedFile;
 
 class BonusController extends BaseController {
     public $modelClass = CalcBonus::class;
@@ -75,13 +88,13 @@ class BonusController extends BaseController {
 //                $showCFLX = true;
 //            }
 //        }
-        $wallet[] = ['walletType' => 'bonus', 'walletName' => 'Member bonus', 'amount' => Tool::formatPrice($data['BONUS'])];//会员奖金
-        $wallet[] = ['walletType' => 'cash', 'walletName' => 'Member Ecoin', 'amount' => Tool::formatPrice($data['CASH'])];//会员余额
+        $wallet[] = ['walletType' => 'bonus', 'walletName' => Yii::t('app', 'memberBonus'), 'amount' => Tool::formatPrice($data['BONUS'])];//会员奖金
+        $wallet[] = ['walletType' => 'cash', 'walletName' => Yii::t('app', 'memberEcoin'), 'amount' => Tool::formatPrice($data['CASH'])];//会员余额
 //        $wallet[] = ['walletType' => 'point', 'walletName' => '会员积分', 'amount' => Tool::formatPrice($data['RECONSUME_POINTS'])];
 //        $wallet[] = ['walletType' => 'exchange', 'walletName' => 'Exchange points', 'amount' => Tool::formatPrice($data['EXCHANGE_POINTS'])];//兑换点数
 //        $wallet[] = ['walletType' => 'tourism_points', 'walletName' => 'Travel points', 'amount' => Tool::formatPrice($data['TOURISM_POINTS'])];//旅游积分
-        $wallet[] = ['walletType' => 'garage_points', 'walletName' => 'Car points', 'amount' => Tool::formatPrice($data['GARAGE_POINTS'])];//车奖积分
-        $wallet[] = ['walletType' => 'villa_points', 'walletName' => 'Villa points', 'amount' => Tool::formatPrice($data['VILLA_POINTS'])];//房奖积分
+        $wallet[] = ['walletType' => 'garage_points', 'walletName' => Yii::t('app', 'carPoints'), 'amount' => Tool::formatPrice($data['GARAGE_POINTS'])];//车奖积分
+        $wallet[] = ['walletType' => 'villa_points', 'walletName' => Yii::t('app', 'villaPoints'), 'amount' => Tool::formatPrice($data['VILLA_POINTS'])];//房奖积分
 //        if ($showCFLX) {
 //            $wallet[] = ['walletType' => 'cf', 'walletName' => '福利积分一', 'amount' => Tool::formatPrice($data['CF'])];
 //            $wallet[] = ['walletType' => 'lx', 'walletName' => '福利积分二', 'amount' => Tool::formatPrice($data['LX'])];
@@ -122,9 +135,7 @@ class BonusController extends BaseController {
             return static::notice(['user' => [],'team'=>[]]);
         }
         // 判断当前时间,是否临近封期,否则隐藏
-        $nowTs = time();
-        $currentPeriodEnd = Period::getEndTime($periodNum);
-        if ($nowTs + 432000 <= $currentPeriodEnd || $currentPeriodEnd < $nowTs || ($periodNum % 2)) {
+        if ($periodNum % 2!=0) {
             return static::notice(['user' => [],'team'=>[]]);
         }
         $userInfo = User::getEnCodeInfo($userId);
@@ -223,9 +234,16 @@ class BonusController extends BaseController {
             }
         }
         if ($createAt) {
-            $condition .= " AND CREATED_AT>:CREATED_START AND CREATED_AT<:CREATED_END";
-            $params[':CREATED_START'] = Date::utcToTime($createAt[0]);
-            $params[':CREATED_END'] = Date::utcToTime($createAt[1])+86399;
+            $createAtStart = $createAt[0] ?? '';
+            $createAtEnd = $createAt[1] ?? '';
+            if ($createAtStart) {
+                $condition .= " AND CREATED_AT>:CREATED_START";
+                $params[':CREATED_START'] = Date::utcToTime($createAtStart);
+            }
+            if ($createAtEnd) {
+                $condition .= " AND CREATED_AT<:CREATED_END";
+                $params[':CREATED_END'] = Date::utcToTime($createAtEnd)+86399;
+            }
         }
         if($remark){
             $condition .= " AND REMARK LIKE :REMARK";
@@ -289,7 +307,7 @@ class BonusController extends BaseController {
             if($walletType != 'cash') {
                 foreach ($data['list'] as $key => $value) {
                     if ($value['DEAL_TYPE_IS_PRESET'] == 0) {
-                        $data['list'][$key]['DEAL_TYPE_NAME'] = $value['AMOUNT'] > 0 ? 'increase' : 'reduce'; // 增加 减少
+                        $data['list'][$key]['DEAL_TYPE_NAME'] = $value['AMOUNT'] > 0 ? Yii::t('app', 'increase') : Yii::t('app', 'reduce');
                     } else {
                         $data['list'][$key]['DEAL_TYPE_NAME'] = DealType::getAllTypesForShow()[$value['DEAL_TYPE_ID']]['TYPE_NAME'] ?? '';
                     }
@@ -306,8 +324,8 @@ class BonusController extends BaseController {
                 if(!$value['DEAL_TYPE_ID']) continue;
                 $dealType = DealType::getAllTypesForShow()[$value['DEAL_TYPE_ID']];
                 if($dealType['IS_PRESET']==0){
-                    $dealTypes['1'] = 'increase';// 增加
-                    $dealTypes['2'] = 'reduce';// 扣除
+                    $dealTypes['1'] = Yii::t('app', 'increase');// 增加
+                    $dealTypes['2'] = Yii::t('app', 'reduce');// 扣除
                 }else{
                     $dealLists[$key]['DEAL_TYPE_NAME'] = $dealType['TYPE_NAME']??'';
                     $dealTypes[$value['DEAL_TYPE_ID']] = $dealLists[$key]['DEAL_TYPE_NAME'];
@@ -332,11 +350,11 @@ class BonusController extends BaseController {
         //是否近期期数
         $showFlowPeriodNum = Cache::getSystemConfig()['showFlowPeriodNum']['VALUE'];
         $periodArr = Period::getNearlySendPeriodNum($showFlowPeriodNum);
-        if(!in_array($periodNum,$periodArr)) return static::notice('Cannot view this period',400); // 该期不能查看
+        if(!in_array($periodNum,$periodArr)) return static::notice(Yii::t('app', 'cannotViewThisPeriod'), 400);
         //增加明细开关控制(0 只显示总奖金 1 全部显示)
         $flowBonusSwitch = Cache::getSystemConfig()['flowBonusSwitch']['VALUE'];
         $data = $this->_periodBonus($periodNum,$flowBonusSwitch);
-        if(!$data) return static::notice('当期无奖金记录',400);
+        if(!$data) return static::notice(Yii::t('app', 'bonusRecordDoesNotExists'), 400);
         return static::notice($data);
     }
 
@@ -705,13 +723,13 @@ class BonusController extends BaseController {
         $condition = '';
         $params = [];
         if (!$periodNum) {
-            return static::notice('请选择期数',400);
+            return static::notice(Yii::t('app', 'pleaseSelectThePeriod'), 400);
         }
         if ($periodNum) {
             $showDecPeriodNum = Cache::getSystemConfig()['showDecPeriodNum']['VALUE'];
             $periodNums = Period::find()->where('IS_SENT=:IS_SENT',[':IS_SENT' => Period::SEND_FINISH])->select('PERIOD_NUM')->limit($showDecPeriodNum)->orderBy('PERIOD_NUM DESC')->asArray()->all();
             if(!in_array($periodNum,array_column($periodNums,'PERIOD_NUM'))){
-                return static::notice('该期无法查看',400);
+                return static::notice(Yii::t('app', 'cannotViewThisPeriod'), 400);
             }
             $condition .= ' AND PERIOD_NUM=:PERIOD_NUM';
             $params[':PERIOD_NUM'] = $periodNum;
@@ -745,13 +763,13 @@ class BonusController extends BaseController {
         $condition = '';
         $params = [];
         if (!$periodNum) {
-            return static::notice('请选择期数',400);
+            return static::notice(Yii::t('app', 'pleaseSelectThePeriod'), 400);
         }
         if ($periodNum) {
             $showDecPeriodNum = Cache::getSystemConfig()['showDecPeriodNum']['VALUE'];
             $periodNums = Period::find()->where('IS_SENT=:IS_SENT',[':IS_SENT' => Period::SEND_FINISH])->select('PERIOD_NUM')->limit($showDecPeriodNum)->orderBy('PERIOD_NUM DESC')->asArray()->all();
             if(!in_array($periodNum,array_column($periodNums,'PERIOD_NUM'))){
-                return static::notice('该期无法查看',400);
+                return static::notice(Yii::t('app', 'cannotViewThisPeriod'), 400);
             }
             $condition .= ' AND PERIOD_NUM=:PERIOD_NUM';
             $params[':PERIOD_NUM'] = $periodNum;
@@ -782,7 +800,7 @@ class BonusController extends BaseController {
         $periodInfo = $period->setPeriodNum($periodNum);
         $yearMonth = $period->getYearMonth($periodNum);
         if (!$period->isSent($periodNum)) {
-            return static::notice('Cannot view this period',400); // 该期不能查看
+            return static::notice(Yii::t('app', 'cannotViewThisPeriod'), 400);
         }
         $data = CalcBonus::findUseSlaves()->yearMonth($yearMonth)->select('BONUS_QY,BONUS_YC,BONUS_FX,BONUS_LS,BONUS_CF,BONUS_LX,BONUS_FL')->where('PERIOD_NUM=:PERIOD_NUM AND USER_ID=:USER_ID', [':PERIOD_NUM' => $periodNum, ':USER_ID' => \Yii::$app->user->id])->asArray()->one();
         return static::notice(['period' => $periodInfo, 'bonus' => $data]);
@@ -797,7 +815,7 @@ class BonusController extends BaseController {
         $yearMonth = \Yii::$app->request->get('yearMonth');
         if ($yearMonth) {
             if (!Date::isYearMonth($yearMonth)) {
-                return static::notice('无效参数', 400);
+                return static::notice(Yii::t('app', 'invalidParameter'), 400);
             }
         } else {
             $period = Period::instance();
@@ -825,7 +843,7 @@ class BonusController extends BaseController {
         $yearMonth = \Yii::$app->request->get('yearMonth');
         if ($yearMonth) {
             if (!Date::isYearMonth($yearMonth)) {
-                return static::notice('无效参数', 400);
+                return static::notice(Yii::t('app', 'invalidParameter'), 400);
             }
         } else {
             $period = Period::instance();
@@ -864,12 +882,12 @@ class BonusController extends BaseController {
             $formModel = new WithdrawForm();
             $formModel->scenario = 'addByUser';
             if ($formModel->load(\Yii::$app->request->post(), '') && $formModel->add()) {
-                return static::notice('Withdrawal application has been submitted, please wait for review.'); // 提现申请已提交,请等待审核
+                return static::notice(Yii::t('app', 'withdrawalApplicationHasBeenSubmitted'), 400);
             } else {
                 return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
             }
         } else {
-            return static::notice('Illegal request', 400); // 非法请求
+            return static::notice(Yii::t('app', 'illegalRequest'), 400);
         }
     }
 
@@ -884,12 +902,12 @@ class BonusController extends BaseController {
             $formModel = new WithdrawForm();
             $formModel->scenario = 'backByUser';
             if ($formModel->load(\Yii::$app->request->post(), '') && $formModel->backByUser()) {
-                return static::notice('提现已退回');
+                return static::notice(Yii::t('app', 'withdrawHasBeenBacked'));
             } else {
                 return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
             }
         } else {
-            return static::notice('Illegal request', 400); // 非法请求
+            return static::notice(Yii::t('app', 'illegalRequest'), 400);
         }
     }
 
@@ -901,23 +919,23 @@ class BonusController extends BaseController {
     public function actionChkWithdrawUser() {
         $uid = \Yii::$app->user->id;
         if (!Info::isVerified($uid)) {
-            return static::notice('未实名验证无法提现', 400);
+            return static::notice(Yii::t('app', 'withdrawDoesNotAllowedOfAuthentication'), 400);
         }
         $userInfo = UserInfo::findOneAsArray('USER_ID=:USER_ID', [':USER_ID' => $uid], 'IS_BIND,IS_BIND_MAIN,IS_AUTO_WITHDRAW,REG_TYPE,TRANSFER_PROP');
         if ($userInfo['IS_BIND'] == 1 && $userInfo['IS_BIND_MAIN'] == 0) {
-            return static::notice('附属会员无法提现', 400);
+            return static::notice(Yii::t('app', 'withdrawDoesNotAllowedOfSubsidiaryMember'), 400);
         }
         if ($userInfo['IS_AUTO_WITHDRAW'] == 1) {
-            return static::notice('已开启自动提现,如需手动提现请关闭自动提现', 400);
+            return static::notice(Yii::t('app', 'autoWithdrawHasBeenOpened'), 400);
         }
         if (!Withdraw::allowWithdraw()) {
-            return static::notice('未到提现日期,请在每月挂网后第一周申请提现', 400);
+            return static::notice(Yii::t('app', 'withdrawNotAllowOfDate'), 400);
         }
         if (Withdraw::hasThisMonthWithdraw($uid)) {
-            return static::notice('提现失败,每月只可以提现一次', 400);
+            return static::notice(Yii::t('app', 'withdrawAllowOnceOfMonth'), 400);
         }
         if (Withdraw::existWaitAudit($uid)) {
-            return static::notice('提现失败,您存在未审核的提现记录', 400);
+            return static::notice(Yii::t('app', 'withdrawRecordHasNotVerify'), 400);
         }
         //是否显示服务协议
         $regType = RegType::findOneAsArray('ID=:ID', [':ID' => $userInfo['REG_TYPE']], 'IS_PACT');
@@ -945,10 +963,10 @@ class BonusController extends BaseController {
     public function actionCollectBind() {
         $transferBonusForm = new TransferBonusForm();
         if ($totals=$transferBonusForm->collectBind(\Yii::$app->user->id)) {
-            return static::notice('归集完成,归集金额'.$totals);
+            return static::notice(Yii::t('app', 'notionalPoolingAmountFinished') . $totals);
         } else {
             if($totals==0){
-                return static::notice('归集完成,归集金额'.$totals);
+                return static::notice(Yii::t('app', 'notionalPoolingAmountFinished') . $totals);
             }else{
                 return static::notice(Form::formatErrorsForApi($transferBonusForm->getErrors()), 400);
             }
@@ -964,13 +982,13 @@ class BonusController extends BaseController {
         $id = \Yii::$app->request->get('id');
         $withdraw = Withdraw::findOneAsArray('ID=:ID', [':ID' => $id]);
         if (!$withdraw) {
-            return static::notice('The data does not exist', 400); // 数据不存在
+            return static::notice(Yii::t('app', 'dataDoesNotExists'), 400);
         }
         $uploadInvoiceTip = Cache::getSystemConfig()['uploadInvoiceTip']['VALUE'];
         if ($withdraw['AUDIT_STATUS'] == Withdraw::STATUS_APPLIED) {
             return static::notice(['addInvoiceTips' => $uploadInvoiceTip]);
         } else {
-            return static::notice('该提现记录无法上传发票', 400);
+            return static::notice(Yii::t('app', 'withdrawDoesNotUploadInvoice'), 400);
         }
     }
 
@@ -984,7 +1002,7 @@ class BonusController extends BaseController {
         $id = \Yii::$app->request->get('id');
         $withdraw = Withdraw::findOneAsArray('ID=:ID', [':ID' => $id]);
         if (!$withdraw) {
-            return static::notice('The data does not exist', 400); // 数据不存在
+            return static::notice(Yii::t('app', 'dataDoesNotExists'), 400);
         }
         if (\Yii::$app->request->isPost) {
             $formModel = new UploadForm();
@@ -995,7 +1013,7 @@ class BonusController extends BaseController {
             //$formModel->token = \Yii::$app->request->post('uploadToken');
             $formModel->token = \Yii::$app->request->request('uploadToken');
             if ($formModel->file && $formModel->upload()) {
-                return static::notice('Successful');
+                return static::notice(Yii::t('app', 'successfully'));
             } else {
                 return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
             }
@@ -1011,7 +1029,7 @@ class BonusController extends BaseController {
         $id = \Yii::$app->request->get('id');
         $uploads = Uploads::findOneAsArray('ID=:ID', [':ID' => $id],'URL');
         if (!$uploads) {
-            return static::notice('The data does not exist', 400); // 数据不存在
+            return static::notice(Yii::t('app', 'dataDoesNotExists'), 400);
         }
         return static::notice($uploads['URL']);
     }
@@ -1034,7 +1052,7 @@ class BonusController extends BaseController {
         $yearMonth = \Yii::$app->request->get('yearMonth');
         if ($yearMonth) {
             if (!Date::isYearMonth($yearMonth)) {
-                return static::notice('无效参数', 400);
+                return static::notice(Yii::t('app', 'invalidParameter'), 400);
             }
         } else {
             $period = Period::instance();
@@ -1088,7 +1106,7 @@ class BonusController extends BaseController {
         if (\Yii::$app->request->isPost) {
             $formModel = new TransferForm();
             if ($formModel->load(\Yii::$app->request->post(), '') && $formModel->transfer()) {
-                return static::notice('Transfer success'); // 转账成功
+                return static::notice(Yii::t('app', 'successfully'));
             } else {
                 return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
             }
@@ -1103,11 +1121,11 @@ class BonusController extends BaseController {
     public function actionChkTransferUser() {
         $uid = \Yii::$app->user->id;
         if (!Info::isVerified($uid)) {
-            return static::notice('未实名验证无法转账', 400);
+            return static::notice(Yii::t('app', 'transferDoesNotAllowedOfAuthentication'), 400);
         }
         $userInfo = UserInfo::findOneAsArray('USER_ID=:USER_ID', [':USER_ID' => $uid], 'ALLOW_TRANSFER,TRANSFER_PROP');
         if($userInfo['ALLOW_TRANSFER']==0){
-            return static::notice('您不允许转账', 400);
+            return static::notice(Yii::t('app', 'transferDoesNotAllowed'), 400);
         }
         $isCanTransferProp = Cache::getSystemConfig()['isCanTransferProp']['VALUE'];
         if($isCanTransferProp==0){
@@ -1128,7 +1146,7 @@ class BonusController extends BaseController {
         $yearMonth = \Yii::$app->request->get('yearMonth');
         if ($yearMonth) {
             if (!Date::isYearMonth($yearMonth)) {
-                return static::notice('无效参数', 400);
+                return static::notice(Yii::t('app', 'invalidParameter'), 400);
             }
             $condition .= ' AND CALC_MONTH=:CALC_MONTH';
             $params['CALC_MONTH'] = $yearMonth;

+ 19 - 18
frontendApi/modules/v1/controllers/ConfigController.php

@@ -17,6 +17,7 @@ use common\models\forms\UserConfigForm;
 use common\models\ReceiveAddress;
 use common\models\Region;
 use common\models\UserInfo;
+use Yii;
 use yii\base\Exception;
 
 class ConfigController extends BaseController
@@ -54,7 +55,7 @@ class ConfigController extends BaseController
             $formModel = new UserConfigForm();
             $formModel->scenario = 'autoWithdraw';
             if($formModel->load(\Yii::$app->request->post(), '') && $formModel->autoWithdraw()){
-                return static::notice('开启关闭自动提现成功');
+                return static::notice(Yii::t('app', 'autoWithdrawHasBeenClosed'));
             } else {
                 return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
             }
@@ -73,7 +74,7 @@ class ConfigController extends BaseController
             $formModel = new UserConfigForm();
             $formModel->scenario = 'allowReconsumeSms';
             if($formModel->load(\Yii::$app->request->post(), '') && $formModel->allowReconsumeSms()){
-                return static::notice('开启关闭复销短信提醒成功');
+                return static::notice(Yii::t('app', 'closeMessageSendSuccessfully'));
             } else {
                 return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
             }
@@ -122,9 +123,9 @@ class ConfigController extends BaseController
     public function actionReceiveAddressAdd() {
         Region::updateToCache();
         if(\Yii::$app->request->isPost) {
-            return parent::edit(ReceiveAddressForm::class, 'Added Successfully', 'userAdd', ['edit']);
+            return parent::edit(ReceiveAddressForm::class, Yii::t('app', 'successfully'), 'userAdd', ['edit']);
         }
-        return static::notice('Illegal request', 400); // 非法请求
+        return static::notice(Yii::t('app', 'illegalRequest'), 400);
     }
 
     /**
@@ -134,9 +135,9 @@ class ConfigController extends BaseController
      */
     public function actionReceiveAddressEdit() {
         if(\Yii::$app->request->isPost) {
-            return parent::edit(ReceiveAddressForm::class, 'Modified Successfully', 'userEdit', ['edit']);
+            return parent::edit(ReceiveAddressForm::class, Yii::t('app', 'successfully'), 'userEdit', ['edit']);
         }
-        return static::notice('Illegal request', 400); // 非法请求
+        return static::notice(Yii::t('app', 'illegalRequest'), 400);
     }
 
     /**
@@ -146,9 +147,9 @@ class ConfigController extends BaseController
      */
     public function actionReceiveAddressDefault() {
         if(\Yii::$app->request->isPost) {
-            return parent::edit(ReceiveAddressForm::class, '添加收货地址成功', 'userIsDefault', ['edit']);
+            return parent::edit(ReceiveAddressForm::class, Yii::t('app', 'successfully'), 'userIsDefault', ['edit']);
         }
-        return static::notice('Illegal request', 400); // 非法请求
+        return static::notice(Yii::t('app', 'illegalRequest'), 400);
     }
 
     /**
@@ -165,12 +166,12 @@ class ConfigController extends BaseController
                     $model = ReceiveAddress::find()->where('USER_ID=:USER_ID', [':USER_ID'=>\Yii::$app->user->id])->one();
                     $model->IS_DEFAULT = 1;
                     if (!$model->save()){
-                        throw new Exception('更新默认地址失败');
+                        throw new Exception(Yii::t('app', 'changeDefaultAddressFailed'));
                     }
                 }
             } , true);
         }
-        return static::notice('Illegal request', 400); // 非法请求
+        return static::notice(Yii::t('app', 'illegalRequest'), 400);
     }
 
     /**
@@ -215,9 +216,9 @@ class ConfigController extends BaseController
     public function actionBaReceiveAddressAdd() {
         Region::updateToCache();
         if (\Yii::$app->request->isPost) {
-            return parent::edit(BaReceiveAddressForm::class, 'Added Successfully', 'userAdd', ['edit']);
+            return parent::edit(BaReceiveAddressForm::class, Yii::t('app', 'successfully'), 'userAdd', ['edit']);
         }
-        return static::notice('Illegal request', 400); // 非法请求
+        return static::notice(Yii::t('app', 'illegalRequest'), 400);
     }
 
     /**
@@ -227,9 +228,9 @@ class ConfigController extends BaseController
      */
     public function actionBaReceiveAddressEdit() {
         if(\Yii::$app->request->isPost) {
-            return parent::edit(BaReceiveAddressForm::class, 'Modified Successfully', 'userEdit', ['edit']);
+            return parent::edit(BaReceiveAddressForm::class, Yii::t('app', 'successfully'), 'userEdit', ['edit']);
         }
-        return static::notice('Illegal request', 400); // 非法请求
+        return static::notice(Yii::t('app', 'illegalRequest'), 400);
     }
 
     /**
@@ -239,9 +240,9 @@ class ConfigController extends BaseController
      */
     public function actionBaReceiveAddressDefault() {
         if(\Yii::$app->request->isPost) {
-            return parent::edit(BaReceiveAddressForm::class, '添加收货地址成功', 'userIsDefault', ['edit']);
+            return parent::edit(BaReceiveAddressForm::class, Yii::t('app', Yii::t('app', 'successfully')), 'userIsDefault', ['edit']);
         }
-        return static::notice('Illegal request', 400); // 非法请求
+        return static::notice(Yii::t('app', 'illegalRequest'), 400);
     }
 
     /**
@@ -260,12 +261,12 @@ class ConfigController extends BaseController
                         $model = BaReceiveAddress::find()->where('USER_ID=:USER_ID', [':USER_ID' => \Yii::$app->getUser()->getId()])->one();
                         $model->IS_DEFAULT = 1;
                         if (!$model->save()) {
-                            throw new Exception('更新默认地址失败');
+                            throw new Exception(Yii::t('app', 'changeDefaultAddressFailed'));
                         }
                     }
                 }
             } , true);
         }
-        return static::notice('Illegal request', 400); // 非法请求
+        return static::notice(Yii::t('app', 'illegalRequest'), 400);
     }
 }

+ 8 - 1
frontendApi/modules/v1/controllers/DashboardController.php

@@ -21,6 +21,7 @@ use common\models\Period;
 use common\models\ReconsumePool;
 use common\models\RemainPv;
 use common\models\Order;
+use Yii;
 
 class DashboardController extends BaseController
 {
@@ -56,6 +57,9 @@ class DashboardController extends BaseController
             $empLvName = Cache::getEmpLevelConfig()[$baseInfo['SHOW_EMP_LV']]['LEVEL_NAME'];
         }
 
+        $crownLv = $baseInfo['CROWN_LV'];
+        $crownLvName = $baseInfo['CROWN_LV_NAME'];
+
         //会员级别
         $decLv = $baseInfo['DEC_LV'];
         $decLvName = $baseInfo['DEC_LV_NAME'];
@@ -119,12 +123,15 @@ class DashboardController extends BaseController
             'nowTime' => $nowTime,
             'empLv'=>$empLv,
             'empLvName'=>$empLvName,
+            'crownLv'=>$crownLv,
+            'crownLvName'=>$crownLvName,
             'decLv'=>$decLv,
             'decLvName'=>$decLvName,
             'slides'=>Ad::findUseSlaves()->select('ID,IMAGE,LID,TITLE,CONTENT,TYPE')->where('LID=:LID AND STATUS=1', [':LID'=>'7EFF6260A16C3CC7E053693418AC03E4'])->orderBy('SORT ASC')->asArray()->all(),
             'news'=>$news,
 //            'periodNum'=>'Period '.$periodNum.' ,'.$curYM['CALC_MONTH'].'月第'.$wkrd.'周,共'.$weeks.'周',
-            'periodNum'=>$periodNum.' ,'.$wkrd.' PC of '. $monthArray[$curYM['CALC_MONTH']],
+//            'periodNum'=>$periodNum.', '.$wkrd.' PC of '. $monthArray[$curYM['CALC_MONTH']],
+            'periodNum'=>$periodNum . ', ' . $wkrd . ' '. Yii::t('app', 'pcOf') . ' '. $monthArray[$curYM['CALC_MONTH']],
             'myRemainPv'=>$totalRemainPv,
             'activeEnd'=>$activeEnd
         ]);

+ 34 - 25
frontendApi/modules/v1/controllers/FinanceController.php

@@ -33,6 +33,7 @@ use common\models\Uploads;
 use common\models\UserInfo;
 use common\models\Withdraw;
 use common\models\UserBonus;
+use Yii;
 use yii\helpers\Json;
 use yii\web\UploadedFile;
 
@@ -70,7 +71,7 @@ class FinanceController extends BaseController {
         $yearMonth = \Yii::$app->request->get('yearMonth');
         if ($yearMonth) {
             if (!Date::isYearMonth($yearMonth)) {
-                return static::notice('无效参数', 400);
+                return static::notice(Yii::t('app', 'invalidParameter'), 400);
             }
         } else {
             $period = Period::instance();
@@ -116,7 +117,7 @@ class FinanceController extends BaseController {
             switch ($checkStatus) {
                 case 0:
                     if ($formModel->load(\Yii::$app->request->post(), '') && $formModel->add()) {
-                        $rtn =  static::notice('Withdrawal application has been submitted, please wait for review.'); // 提现申请已提交,请等待审核
+                        $rtn =  static::notice(Yii::t('app', 'withdrawalApplicationHasBeenSubmitted'));
                     } else {
                         $rtn = static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
                     }
@@ -128,14 +129,14 @@ class FinanceController extends BaseController {
                     $rtn = static::notice(Form::formatErrorsForApi($formModel->getErrors()), 424);
                     break;
                 default:
-                    $rtn = static::notice('状态错误', 400);
+                    $rtn = static::notice(Yii::t('app', 'statueError'), 400);
                     break;
             }
 
             return $rtn;
 
         } else {
-            return static::notice('Illegal request', 400); // 非法请求
+            return static::notice(Yii::t('app', 'illegalRequest'), 400);
         }
     }
 
@@ -159,17 +160,17 @@ class FinanceController extends BaseController {
      * @throws \yii\web\HttpException
      */
     public function actionWithdrawBack() {
-        return static::notice('Illegal request', 400); // 非法请求
+        return static::notice(Yii::t('app', 'illegalRequest'), 400);
         if (\Yii::$app->request->isPost) {
             $formModel = new WithdrawForm();
             $formModel->scenario = 'backByUser';
             if ($formModel->load(\Yii::$app->request->post(), '') && $formModel->backByUser()) {
-                return static::notice('提现已退回');
+                return static::notice(Yii::t('app', 'withdrawHasBeenBacked'));
             } else {
                 return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
             }
         } else {
-            return static::notice('Illegal request', 400); // 非法请求
+            return static::notice(Yii::t('app', 'illegalRequest'), 400);
         }
     }
 
@@ -181,7 +182,7 @@ class FinanceController extends BaseController {
     public function actionChkWithdrawUser() {
         $uid = \Yii::$app->user->id;
         if (!Info::isVerified($uid)) {
-            return static::notice('未实名验证无法提现', 400);
+            return static::notice(Yii::t('app', 'withdrawDoesNotAllowedOfAuthentication'), 400);
         }
         $userInfo = UserInfo::findOneAsArray('USER_ID=:USER_ID', [':USER_ID' => $uid], 'IS_BIND,IS_BIND_MAIN,IS_AUTO_WITHDRAW,REG_TYPE,TRANSFER_PROP');
 //        if ($userInfo['IS_BIND'] == 1 && $userInfo['IS_BIND_MAIN'] == 0) {
@@ -232,10 +233,10 @@ class FinanceController extends BaseController {
     public function actionCollectBind() {
         $transferBonusForm = new TransferBonusForm();
         if ($totals=$transferBonusForm->collectBind(\Yii::$app->user->id)) {
-            return static::notice('归集完成,归集金额'.$totals);
+            return static::notice(Yii::t('app', 'notionalPoolingAmountFinished') . $totals);
         } else {
             if($totals==0){
-                return static::notice('归集完成,归集金额'.$totals);
+                return static::notice(Yii::t('app', 'notionalPoolingAmountFinished') . $totals);
             }else{
                 return static::notice(Form::formatErrorsForApi($transferBonusForm->getErrors()), 400);
             }
@@ -251,13 +252,13 @@ class FinanceController extends BaseController {
         $id = \Yii::$app->request->get('id');
         $withdraw = Withdraw::findOneAsArray('ID=:ID', [':ID' => $id]);
         if (!$withdraw) {
-            return static::notice('The data does not exist', 400); // 数据不存在
+            return static::notice(Yii::t('app', 'dataDoesNotExists'), 400);
         }
         $uploadInvoiceTip = Cache::getSystemConfig()['uploadInvoiceTip']['VALUE'];
         if ($withdraw['AUDIT_STATUS'] == Withdraw::STATUS_APPLIED) {
             return static::notice(['addInvoiceTips' => $uploadInvoiceTip]);
         } else {
-            return static::notice('该提现记录无法上传发票', 400);
+            return static::notice(Yii::t('app', 'withdrawDoesNotUploadInvoice'), 400);
         }
     }
 
@@ -271,7 +272,7 @@ class FinanceController extends BaseController {
         $id = \Yii::$app->request->get('id');
         $withdraw = Withdraw::findOneAsArray('ID=:ID', [':ID' => $id]);
         if (!$withdraw) {
-            return static::notice('The data does not exist', 400); // 数据不存在
+            return static::notice(Yii::t('app', 'dataDoesNotExists'), 400);
         }
         if (\Yii::$app->request->isPost) {
             $formModel = new UploadForm();
@@ -298,7 +299,7 @@ class FinanceController extends BaseController {
         $id = \Yii::$app->request->get('id');
         $uploads = Uploads::findOneAsArray('ID=:ID', [':ID' => $id],'URL');
         if (!$uploads) {
-            return static::notice('The data does not exist', 400); // 数据不存在
+            return static::notice(Yii::t('app', 'dataDoesNotExists'), 400);
         }
         return static::notice($uploads['URL']);
     }
@@ -323,7 +324,7 @@ class FinanceController extends BaseController {
         $yearMonth = \Yii::$app->request->get('yearMonth');
         if ($yearMonth) {
             if (!Date::isYearMonth($yearMonth)) {
-                return static::notice('无效参数', 400);
+                return static::notice(Yii::t('app', 'invalidParameter'), 400);
             }
         } else {
             $period = Period::instance();
@@ -381,12 +382,20 @@ class FinanceController extends BaseController {
         }
         $dateRange = \Yii::$app->request->get('dateRange', '');
         if ($dateRange) {
-            $condition .= " AND CREATED_AT>:CREATED_START AND CREATED_AT<:CREATED_END";
-            $params[':CREATED_START'] = Date::utcToTime($dateRange[0]);
-            $params[':CREATED_END'] = Date::utcToTime($dateRange[1]);
+            $createAtStart = $dateRange[0] ?? '';
+            $createAtEnd = $dateRange[1] ?? '';
+            if ($createAtStart) {
+                $condition .= " AND CREATED_AT>:CREATED_START";
+                $params[':CREATED_START'] = Date::utcToTime($createAtStart);
+            }
+            if ($createAtEnd) {
+                $condition .= " AND CREATED_AT<:CREATED_END";
+                $params[':CREATED_END'] = Date::utcToTime($createAtEnd) + 86399;
+            }
         }
 
 
+
         //获取可以查看几期流水
         $showTransferPeriodNum = Cache::getSystemConfig()['showTransferPeriodNum']['VALUE'];
         $periodArr = Period::getNearlyPeriodNum($showTransferPeriodNum);
@@ -428,7 +437,7 @@ class FinanceController extends BaseController {
             $formModel = new TransferForm();
             $formModel->scenario = 'transfer';
             if ($formModel->load(\Yii::$app->request->post(), '') && $formModel->transfer()) {
-                return static::notice('Transfer success'); // 转账成功
+                return static::notice(Yii::t('app', 'successfully'));
             } else {
                 return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
             }
@@ -457,15 +466,15 @@ class FinanceController extends BaseController {
     public function actionChkTransferUser() {
         $uid = \Yii::$app->user->id;
         if (!Info::isVerified($uid)) {
-            return static::notice('未实名验证无法转账', 400);
+            return static::notice(Yii::t('app', 'withdrawDoesNotAllowedOfAuthentication'), 400);
         }
         $sysConfig = Cache::getSystemConfig();
         if(!$sysConfig['transferOpen']['VALUE']){
-            return static::notice('转账功能未启用', 400);
+            return static::notice(Yii::t('app', 'transferDoesNotOpen'), 400);
         }
         $userInfo = UserInfo::findOneAsArray('USER_ID=:USER_ID', [':USER_ID' => $uid], 'ALLOW_TRANSFER,TRANSFER_PROP');
         if($userInfo['ALLOW_TRANSFER']==0){
-            return static::notice('您不允许转账', 400);
+            return static::notice(Yii::t('app', 'transferDoesNotAllowed'), 400);
         }
         $isCanTransferProp = Cache::getSystemConfig()['isCanTransferProp']['VALUE'];
         if($isCanTransferProp==0){
@@ -488,7 +497,7 @@ class FinanceController extends BaseController {
             ];
         }
         if(count($arr)==0){
-            return static::notice('没有可用的转账类型', 400);
+            return static::notice(Yii::t('app', 'transferTypeDoesNotExists'), 400);
         }
         return static::notice([
             'userInfo' => $userInfo,
@@ -531,7 +540,7 @@ class FinanceController extends BaseController {
             $formModel = new RechargeForm();
             $formModel->scenario = 'addByUser';
             if ($formModel->load(\Yii::$app->request->post(), '') && $formModel->add()) {
-                return static::notice('Recharge application has been submitted, please wait for review.');// 充值申请已提交,请等待审核
+                return static::notice(Yii::t('app', 'rechargeApplicationHasBeenSubmitted'));
             } else {
                 return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
             }
@@ -556,7 +565,7 @@ class FinanceController extends BaseController {
             $formModel->token = \Yii::$app->request->request('uploadToken');
             $formModel->rechargeId = $id;
             if ($formModel->file && $formModel->upload()) {
-                return static::notice('Successful');
+                return static::notice(Yii::t('app', 'successfully'));
             } else {
                 return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
             }

+ 10 - 8
frontendApi/modules/v1/controllers/OauthController.php

@@ -106,9 +106,11 @@ class OauthController extends BaseController
      */
     public function actionLogin() {
         $userName = Yii::$app->request->post('userName');
+        $version = Yii::$app->request->post('version', '');
         $model = new LoginForm(
             [
-                'userName' =>$userName
+                'userName' => $userName,
+                'version'  => $version,
             ]
         );
         if ( $model->isLoginVerify() ) {
@@ -125,7 +127,7 @@ class OauthController extends BaseController
                 return static::notice(LoginForm::ERROR_IS_MODIFY_PASSWORD, 403);
             }
 
-            return static::notice(Form::formatErrorsForApi($model->getErrors()), 401);
+            return static::notice(Form::formatErrorsForApi($model->getErrors()), 400);
         }
 
     }
@@ -142,7 +144,7 @@ class OauthController extends BaseController
         if($token){
             return static::notice($token);
         } else {
-            return static::notice('更新Token失败', 401);
+            return static::notice(Yii::t('app', 'refreshTokenFailed'), 401);
         }
     }
 
@@ -158,7 +160,7 @@ class OauthController extends BaseController
         if($token){
             return static::notice($token);
         } else {
-            return static::notice('更新Token失败', 401);
+            return static::notice(Yii::t('app', 'refreshTokenFailed'), 401);
         }
     }
 
@@ -174,7 +176,7 @@ class OauthController extends BaseController
         if($token){
             return static::notice($token);
         } else {
-            return static::notice('更新Token失败', 401);
+            return static::notice(Yii::t('app', 'refreshTokenFailed'), 401);
         }
     }
 
@@ -190,7 +192,7 @@ class OauthController extends BaseController
                 return static::notice($result);
             }
         }
-        return static::notice('Illegal request', 400); // 非法请求
+        return static::notice(Yii::t('app', 'illegalRequest'), 400);
     }
 
     /**
@@ -203,12 +205,12 @@ class OauthController extends BaseController
             $form->scenario = 'noLoginModifyPassword';
             $post = \Yii::$app->request->post();
             if($form->load($post, '') && $result = $form->noLoginModifyPassword()){
-                return static::notice('Password change succeeded'); // 密码修改成功
+                return static::notice(Yii::t('app', 'passwordChangeSucceeded'));
             } else {
                 return static::notice(Form::formatErrorsForApi($form->getErrors()), 400);
             }
         }
-        return static::notice('Illegal request', 400); // 非法请求
+        return static::notice(Yii::t('app', 'illegalRequest'), 400);
     }
 
 }

+ 383 - 97
frontendApi/modules/v1/controllers/ShopController.php

@@ -114,12 +114,14 @@ class ShopController extends BaseController {
 //            'exchange' => 0,
             'tourism_points' => 0,
             'garage_points' => 0,
+            'villa_points' => 0,
         ];
         if ($userBonusResult = UserBonus::findOneAsArray(['USER_ID' => $userId])) {
 //            $userBalance['points'] = $userBonusResult['RECONSUME_POINTS'];
 //            $userBalance['exchange'] = $userBonusResult['EXCHANGE_POINTS'];
             $userBalance['tourism_points'] = $userBonusResult['TOURISM_POINTS'];
             $userBalance['garage_points'] = $userBonusResult['GARAGE_POINTS'];
+            $userBalance['villa_points'] = $userBonusResult['VILLA_POINTS'];
         }
         if ($userCashResult = UserWallet::findOneAsArray(['USER_ID' => $userId])) {
             $userBalance['cash'] = $userCashResult['CASH'];
@@ -271,12 +273,23 @@ class ShopController extends BaseController {
         return static::notice('');
     }
 
+    /**
+     * @throws HttpException
+     */
+    public function actionDecOrderList() {
+        $version = \Yii::$app->request->get('v');
+
+        $data = $version == 2 ? $this->decOrderListV2() : $this->decOrderListV1();
+
+        return static::notice($data);
+    }
+
     /**
      * 我的报单
      * @return mixed
      * @throws HttpException
      */
-    public function actionDecOrderList() {
+    public function decOrderListV1() {
         $condition = ' DO.USER_ID=:USER_ID AND DO.IS_DEL=0';
         $params[':USER_ID'] = \Yii::$app->user->id;
 
@@ -329,6 +342,85 @@ class ShopController extends BaseController {
             $value['TOTAL_AMOUNT'] = Tool::formatPrice($value['REAL_PRICE'] * $value['BUY_NUMS']);
         }
 
+        return $data;
+    }
+
+    /**
+     * 我的报单
+     * @return mixed
+     * @throws HttpException
+     */
+    public function decOrderListV2() {
+        $condition = ' DO.USER_ID=:USER_ID AND DO.IS_DEL=0';
+        $params[':USER_ID'] = \Yii::$app->user->id;
+
+        $orderQuery = DecOrder::find()
+            ->alias('DO')
+            ->where($condition, $params)
+            ->select('DO.DEC_SN,DO.ORDER_SN,DO.CREATED_AT,O.STATUS,O.CONSIGNEE,O.MOBILE,O.PAY_TYPE,O.PAY_AT,O.ADDRESS,O.CITY_NAME,O.LGA_NAME,O.PROVINCE,U.USER_NAME USER_NAME,U.REAL_NAME REAL_NAME,RU.USER_NAME REC_USER_NAME,RU.REAL_NAME REC_REAL_NAME,CU.USER_NAME CON_USER_NAME,CU.REAL_NAME CON_REAL_NAME')
+            ->join('LEFT JOIN', User::tableName() . ' AS U', 'DO.TO_USER_ID=U.ID')
+            ->join('LEFT JOIN', User::tableName() . ' AS RU', 'DO.REC_USER_ID=RU.ID')
+            ->join('LEFT JOIN', User::tableName() . ' AS CU', 'DO.CON_USER_ID=CU.ID')
+            ->join('LEFT JOIN', Order::tableName() . ' AS O', 'O.SN=DO.ORDER_SN')
+            ->orderBy('DO.CREATED_AT DESC');
+
+        // 订单中间表只查询待支付和支付失败的订单
+        $params[':NOT_PAID'] = \Yii::$app->params['orderStatus']['notPaid']['value'];   // 待支付
+        $params[':FAIL_PAID'] = \Yii::$app->params['orderStatus']['failPaid']['value'];   // 支付失败
+        $orderStandardQuery = ApproachDecOrder::find()
+            ->alias('DO')
+            ->where($condition . ' AND (O.STATUS = :NOT_PAID OR O.STATUS = :FAIL_PAID)', $params)
+            ->select('DO.DEC_SN,DO.ORDER_SN,DO.CREATED_AT,O.STATUS,O.CONSIGNEE,O.MOBILE,O.PAY_TYPE,O.PAY_AT,O.ADDRESS,O.CITY_NAME,O.LGA_NAME,O.PROVINCE,U.USER_NAME USER_NAME,U.REAL_NAME REAL_NAME,RU.USER_NAME REC_USER_NAME,RU.REAL_NAME REC_REAL_NAME,CU.USER_NAME CON_USER_NAME,CU.REAL_NAME CON_REAL_NAME')
+            ->join('LEFT JOIN', User::tableName() . ' AS U', 'DO.TO_USER_ID=U.ID')
+            ->join('LEFT JOIN', User::tableName() . ' AS RU', 'DO.REC_USER_ID=RU.ID')
+            ->join('LEFT JOIN', User::tableName() . ' AS CU', 'DO.CON_USER_ID=CU.ID')
+            ->join('LEFT JOIN', ApproachOrder::tableName() . ' AS O', 'O.SN=DO.ORDER_SN')
+            ->orderBy('DO.CREATED_AT DESC');
+
+        $queryAll = $orderQuery->union($orderStandardQuery, true);
+        $query = (new Query())->from(['Q' => $queryAll])->select('Q.*')->distinct()->orderBy(['CREATED_AT' => SORT_DESC]);
+
+        $totalCount = $query->count();
+        $pagination = new Pagination(['totalCount' => $totalCount, 'pageSize' => \Yii::$app->request->get('pageSize')]);
+        $lists = $query->offset($pagination->offset)->limit($pagination->limit)->all();
+
+        $data = [
+            'list' => $lists,
+            'currentPage'=>$pagination->page,
+            'totalPages'=>$pagination->pageCount,
+            'startNum' => $pagination->page * $pagination->pageSize + 1,
+            'totalCount' => $pagination->totalCount,
+            'pageSize' => $pagination->pageSize,
+        ];
+
+        foreach ($data['list'] as &$value) {
+            if (($value['PAY_TYPE'] == 'pay_stack') && in_array($value['STATUS'], [\Yii::$app->params['orderStatus']['notPaid']['value'], \Yii::$app->params['orderStatus']['failPaid']['value']])) {
+                // 订单中间表
+                $orderGoods = ApproachOrderGoods::findAllAsArray('ORDER_SN=:ORDER_SN', [':ORDER_SN' => $value['ORDER_SN']], 'SKU_CODE,GOODS_TITLE,BUY_NUMS,TAX_RATE,REAL_PV,REAL_PRICE');
+            } else {
+                // 订单表
+                $orderGoods = OrderGoods::findAllAsArray('ORDER_SN=:ORDER_SN', [':ORDER_SN' => $value['ORDER_SN']], 'SKU_CODE,GOODS_TITLE,BUY_NUMS,TAX_RATE,REAL_PV,REAL_PRICE');
+            }
+            $value['hasChildren'] = $orderGoods;
+            $value['PAY_TYPE'] = array_column(ShopGoods::SALE_TYPE, NULL, 'label')[$value['PAY_TYPE']]['name'] ?? '';
+            $value['STATUS'] = \Yii::$app->params['orderStatus'][$value['STATUS']]['label'] ?? '';
+            $value['CREATED_AT'] = $value['CREATED_AT'] ? Date::convert($value['CREATED_AT'],'Y-m-d H:i:s') : '';
+            $value['FULL_ADDRESS'] = trim(implode(', ', [$value['ADDRESS'], $value['CITY_NAME'], $value['LGA_NAME'], Region::getCnName($value['PROVINCE'])]), ', ');
+
+            unset($value['ADDRESS'], $value['CITY_NAME'], $value['LGA_NAME'], $value['PROVINCE']);
+        }
+
+        return $data;
+    }
+
+    /**
+     * @throws HttpException
+     */
+    public function actionOrderList() {
+        $version = \Yii::$app->request->get('v');
+
+        $data = $version == 2 ? $this->orderListV2() : $this->orderListV1();
+
         return static::notice($data);
     }
 
@@ -337,14 +429,14 @@ class ShopController extends BaseController {
      * @return mixed
      * @throws HttpException
      */
-    public function actionOrderList() {
+    public function orderListV1() {
         $uname = Info::getUserNameByUserId(\Yii::$app->user->id);
         $condition = " (USER_ID=:USER_ID OR CREATE_USER='$uname') AND O.IS_DELETE = 0";
         $params[':USER_ID'] = \Yii::$app->user->id;
         $orderQuery = Order::find()
             ->alias('O')
             ->where($condition, $params)
-            ->select('O.*,U.REAL_NAME,SG.CATEGORY_TYPE,OG.REAL_PRICE,OG.TAX_RATE,OG.BUY_NUMS,OG.SKU_CODE,OG.GOODS_TITLE,OG.REAL_PV')
+            ->select('O.*,U.REAL_NAME,SG.CATEGORY_TYPE,OG.REAL_PRICE,OG.TAX_RATE,OG.BUY_NUMS,OG.SKU_CODE,OG.GOODS_TITLE,OG.REAL_PV,SG.COVER')
             ->join('LEFT JOIN', User::tableName() . ' AS U', 'U.ID=O.USER_ID')
             ->join('LEFT JOIN', OrderGoods::tableName() . ' AS OG', 'OG.ORDER_SN=O.SN')
             ->join('LEFT JOIN', ShopGoods::tableName() . ' AS SG', 'SG.ID=OG.GOODS_ID')
@@ -356,7 +448,7 @@ class ShopController extends BaseController {
         $orderStandardQuery = ApproachOrder::find()
             ->alias('O')
             ->where($condition . ' AND (O.STATUS = :NOT_PAID OR O.STATUS = :FAIL_PAID)', $params)
-            ->select('O.*,U.REAL_NAME,SG.CATEGORY_TYPE,OG.REAL_PRICE,OG.TAX_RATE,OG.BUY_NUMS,OG.SKU_CODE,OG.GOODS_TITLE,OG.REAL_PV')
+            ->select('O.*,U.REAL_NAME,SG.CATEGORY_TYPE,OG.REAL_PRICE,OG.TAX_RATE,OG.BUY_NUMS,OG.SKU_CODE,OG.GOODS_TITLE,OG.REAL_PV,SG.COVER')
             ->join('LEFT JOIN', User::tableName() . ' AS U', 'U.ID=O.USER_ID')
             ->join('LEFT JOIN', ApproachOrderGoods::tableName() . ' AS OG', 'OG.ORDER_SN=O.SN')
             ->join('LEFT JOIN', ShopGoods::tableName() . ' AS SG', 'SG.ID=OG.GOODS_ID')
@@ -410,7 +502,68 @@ class ShopController extends BaseController {
             $data['list'][$key]['TOTAL_AMOUNT'] = Tool::formatPrice($value['REAL_PRICE'] * $value['BUY_NUMS']);
         }
 
-        return static::notice($data);
+        return $data;
+    }
+
+    /**
+     * 我的订单
+     * @return mixed
+     * @throws HttpException
+     */
+    public function orderListV2() {
+        $uname = Info::getUserNameByUserId(\Yii::$app->user->id);
+        $condition = " (O.USER_ID=:USER_ID OR O.CREATE_USER='{$uname}') AND O.IS_DELETE = 0";
+        $params[':USER_ID'] = \Yii::$app->user->id;
+        $orderQuery = Order::find()
+            ->alias('O')
+            ->where($condition, $params)
+            ->select('O.ID,O.SN,O.USER_NAME,O.ORDER_TYPE,O.ORDER_AMOUNT,O.STATUS,O.CREATED_AT,O.PAY_TYPE,O.PAY_AT,O.CONSIGNEE,O.MOBILE,O.ADDRESS,O.CITY_NAME,O.LGA_NAME,O.PROVINCE,U.REAL_NAME')
+            ->join('LEFT JOIN', User::tableName() . ' AS U', 'U.ID=O.USER_ID');
+
+        // 订单中间表只查询待支付和支付失败的订单
+        $params[':NOT_PAID'] = \Yii::$app->params['orderStatus']['notPaid']['value'];   // 待支付
+        $params[':FAIL_PAID'] = \Yii::$app->params['orderStatus']['failPaid']['value'];   // 支付失败
+        $orderStandardQuery = ApproachOrder::find()
+            ->alias('O')
+            ->where($condition . ' AND (O.STATUS = :NOT_PAID OR O.STATUS = :FAIL_PAID)', $params)
+            ->select('O.ID,O.SN,O.USER_NAME,O.ORDER_TYPE,O.ORDER_AMOUNT,O.STATUS,O.CREATED_AT,O.PAY_TYPE,O.PAY_AT,O.CONSIGNEE,O.MOBILE,O.ADDRESS,O.CITY_NAME,O.LGA_NAME,O.PROVINCE,U.REAL_NAME')
+            ->join('LEFT JOIN', User::tableName() . ' AS U', 'U.ID=O.USER_ID');
+
+        $queryAll = $orderQuery->union($orderStandardQuery, true);
+        $query = (new Query())->from(['Q' => $queryAll])->select('Q.*')->distinct()->orderBy(['CREATED_AT' => SORT_DESC]);
+
+        $totalCount = $query->count();
+        $pagination = new Pagination(['totalCount' => $totalCount, 'pageSize' => \Yii::$app->request->get('pageSize')]);
+        $lists = $query->offset($pagination->offset)->limit($pagination->limit)->all();
+
+        $data = [
+            'list' => $lists,
+            'currentPage'=>$pagination->page,
+            'totalPages'=>$pagination->pageCount,
+            'startNum' => $pagination->page * $pagination->pageSize + 1,
+            'totalCount' => $pagination->totalCount,
+            'pageSize' => $pagination->pageSize,
+        ];
+
+        foreach ($data['list'] as $key => $value) {
+            if (($value['PAY_TYPE'] == 'pay_stack') && in_array($value['STATUS'], [\Yii::$app->params['orderStatus']['notPaid']['value'], \Yii::$app->params['orderStatus']['failPaid']['value']])) {
+                // 订单中间表
+                $orderGoods = ApproachOrderGoods::findAllAsArray('ORDER_SN=:SN', [':SN' => $value['SN']], 'SKU_CODE,GOODS_TITLE,BUY_NUMS,TAX_RATE,REAL_PV,REAL_PRICE');
+            } else {
+                // 订单表
+                $orderGoods = OrderGoods::findAllAsArray('ORDER_SN=:SN', [':SN' => $value['SN']], 'SKU_CODE,GOODS_TITLE,BUY_NUMS,TAX_RATE,REAL_PV,REAL_PRICE');
+            }
+            $data['list'][$key]['hasChildren'] = $orderGoods;
+            $data['list'][$key]['ORDER_TYPE'] = $value['ORDER_TYPE']=='ZC' ? 'Welcome Pack' : (in_array($value['PAY_TYPE'], ['cash', 'pay_stack']) ? 'Repeat Purchase': 'Points');
+            $data['list'][$key]['PAY_TYPE'] = array_column(ShopGoods::SALE_TYPE, NULL, 'label')[$value['PAY_TYPE']]['name'] ?? '';
+            $data['list'][$key]['STATUS'] = \Yii::$app->params['orderStatus'][$value['STATUS']]['label'] ?? '';
+            $data['list'][$key]['FULL_ADDRESS'] = trim(implode(', ', [$value['ADDRESS'], $value['CITY_NAME'], $value['LGA_NAME'], Region::getCnName($value['PROVINCE'])]), ', ');
+            $data['list'][$key]['SHOW_BV'] = !in_array($value['PAY_TYPE'], [5, 6]);
+
+            unset($data['list'][$key]['ADDRESS'], $data['list'][$key]['CITY_NAME'], $data['list'][$key]['LGA_NAME'], $data['list'][$key]['PROVINCE']);
+        }
+
+        return $data;
     }
 
     /**
@@ -460,11 +613,11 @@ class ShopController extends BaseController {
         if (\Yii::$app->request->isPost) {
             $formModel = new OrderForm();
             $formModel->scenario = 'reconsumeOrder';
-            $formModel->remark = '帮会员复销';
+            $formModel->remark = Yii::t('app', 'reconsume');
             $post = \Yii::$app->request->post();
             $post['type'] = DeclarationForm::TYPE_FX;
             if ($formModel->load($post, '') && $formModel->reconsumeAdd()) {
-                return static::notice('帮会员复消成功');
+                return static::notice(Yii::t('app', 'reconsumeSuccessfully'));
             } else {
                 return static::notice(Form::formatErrorsForApi($formModel->getErrors()),400);
             }
@@ -485,19 +638,19 @@ class ShopController extends BaseController {
 //            ],
             [
                 'name' => '1',
-                'label' => 'Standard Products',//普通商品列表
+                'label' => Yii::t('app', 'standardProducts'),
             ],
 //            [
 //                'name' => '4',
-//                'label' => 'Travel Fund Products',//旅游积分商品
+//                'label' => Yii::t('app', 'travelFundProducts'),
 //            ],
             [
                 'name' => '5',
-                'label' => 'Car Fund Products',//车奖积分商品
+                'label' => Yii::t('app', 'carFundProducts'),
             ],
             [
                 'name' => '6',
-                'label' => 'Villa Fund Products',//房奖积分商品
+                'label' => Yii::t('app', 'villaFundProducts'),
             ],
         ];
         return static::notice($data);
@@ -563,7 +716,7 @@ class ShopController extends BaseController {
             $userName = $value['REAL_NAME'];
             $address = $provinceName . $cityName . $countyName . $value['ADDRESS'];
             $mobile = $value['MOBILE'];
-            $orderAt = Date::convert($value['PAY_AT'],'Y-m-d H:i:s');
+            $orderAt = Date::convert($value['CREATED_AT'],'Y-m-d H:i:s');
 
             // 总价
             $totalAmount = $value['BUY_NUMS'] * $value['REAL_PRICE'];
@@ -578,46 +731,64 @@ class ShopController extends BaseController {
             // 订单详情
             $orderDetails .= <<<EOT
                 <tr>
-                    <td>{$value['SKU_CODE']}</td>
-                    <td>{$value['GOODS_TITLE']}</td>
+                    <td style="text-align: center;">{$value['SKU_CODE']}</td>
+                    <td style="text-align: center;">{$value['GOODS_TITLE']}</td>
                     <td style="text-align: right;">{$productAmount}</td>
-                    <td>{$value['BUY_NUMS']}</td>
-                    <td style="text-align: right;">{$value['TAX_RATE']}</td>
+                    <td style="text-align: right;">{$value['BUY_NUMS']}</td>
+                    <td style="text-align: right;">{$value['TAX_RATE']}%</td>
                     <td style="text-align: right;">{$taxAmount}</td>
                     <td style="text-align: right;">{$totalAmount}</td> 
                 </tr>
 EOT;
         }
 
+        $memberCode = Yii::t('app', 'memberCode');
+        $memberName = Yii::t('app', 'memberName');
+        $memberAddress = Yii::t('app', 'memberAddress');
+        $memberPhone = Yii::t('app', 'memberPhone');
+        $orderCode = Yii::t('app', 'orderCode');
+        $orderDetail = Yii::t('app', 'orderDetail');
+        $productCode = Yii::t('app', 'productCode');
+        $productName = Yii::t('app', 'productName');
+        $productPrice = Yii::t('app', 'productPrice');
+        $quantity = Yii::t('app', 'qty');
+        $taxRate = Yii::t('app', 'taxRate');
+        $totalTax = Yii::t('app', 'totalTax');
+        $totalAmount = Yii::t('app', 'totalAmount');
+        $total = Yii::t('app', 'total');
+        $signature = Yii::t('app', 'signature');
+        $date = Yii::t('app', 'date');
+        $createAt = Yii::t('app', 'createAt');
+
         // 订单基本信息
         $orderBase = <<<ORDER
             <table border="1" style="table-layout: fixed; padding: 10px 20px;" width="100%">
                 <tr>
-                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Member Code</td>
+                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">{$memberCode}</td>
                     <td width="70%">{$userId}</td>
                 </tr>
                 <tr>
-                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Member Name</td>
+                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">{$memberName}</td>
                     <td width="70%">{$userName}</td>
                 </tr>
                 <tr>
-                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Member Address</td>
+                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">{$memberAddress}</td>
                     <td width="70%">{$address}</td>
                 </tr>
                 <tr>
-                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Member Phone</td>
+                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">{$memberPhone}</td>
                     <td width="70%">{$mobile}</td>
                 </tr>
                 <tr>
-                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Order Code</td>
+                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">{$orderCode}</td>
                     <td width="70%">{$orderSn}</td>
                 </tr>
                 <tr>
-                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Creation Time</td>
+                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">{$createAt}</td>
                     <td width="70%">{$orderAt}</td>
                 </tr>
                 <tr>
-                    <td class="bg" style="font-weight: bold; font-size: 14px; text-align: center;">Order Detail</td>
+                    <td class="bg" style="font-weight: bold; font-size: 14px; text-align: center;">{$orderDetail}</td>
                     <td class="bg"></td>
                 </tr>
             </table>
@@ -636,14 +807,14 @@ ORDER;
             <html lang="en">
             <head>
                 <meta charset="UTF-8" />
-                <title>Order detail</title>
+                <title>{$orderDetail}</title>
                 <style>
                     table {
                         border-collapse: collapse;
                     }
                     table td, table th {
                         border: 1px solid #ccc;
-                        padding: 5px 5px;
+                        /*padding: 5px 5px;*/
                         border-collapse: collapse;
                     }
                     /*td {*/
@@ -656,25 +827,25 @@ ORDER;
             </head>
             <body>
                 <div class="content">
-                    <p style="text-align: center; font-weight: bold; font-size: 22px;"><b>Order detail</b><br></p>
+                    <p style="text-align: center; font-weight: bold; font-size: 22px;"><b>{$orderDetail}</b><br></p>
                     <div>
                         <div style="display: block; width: 100%;">
                             {$orderBase}
                             
                             <table border="1" width="100%" style="padding: 10px 5px; text-align: center;">
                                 <tr>
-                                    <th width="15%" style="font-size: 14px; font-weight: bold; text-align: center;">Product Code</th>
-                                    <th width="25%" style="font-size: 14px; font-weight: bold; text-align: center;">Product Name</th>
-                                    <th width="15%" style="font-size: 14px; font-weight: bold; text-align: center;">Product Price</th>
-                                    <th width="10%" style="font-size: 14px; font-weight: bold; text-align: center;">Qty</th>
-                                    <th width="10%" style="font-size: 14px; font-weight: bold; text-align: center;">Tax rate</th>
-                                    <th width="10%" style="font-size: 14px; font-weight: bold; text-align: center;">Tax</th>
-                                    <th width="15%" style="font-size: 14px; font-weight: bold; text-align: center;">Total amount</th>
+                                    <th width="15%" style="font-size: 14px; font-weight: bold; text-align: center;">{$productCode}</th>
+                                    <th width="20%" style="font-size: 14px; font-weight: bold; text-align: center;">{$productName}</th>
+                                    <th width="15%" style="font-size: 14px; font-weight: bold; text-align: center;">{$productPrice}</th>
+                                    <th width="10%" style="font-size: 14px; font-weight: bold; text-align: center;">{$quantity}</th>
+                                    <th width="12%" style="font-size: 14px; font-weight: bold; text-align: center;">{$taxRate}</th>
+                                    <th width="13%" style="font-size: 14px; font-weight: bold; text-align: center;">{$totalTax}</th>
+                                    <th width="15%" style="font-size: 14px; font-weight: bold; text-align: center;">{$totalAmount}</th>
                                 </tr>
                                 {$orderDetails}
                                 <tr>
-                                    <td colspan="3">Total</td>
-                                    <td>{$orderNums}</td>
+                                    <td colspan="3">{$total}</td>
+                                    <td style="text-align: right;">{$orderNums}</td>
                                     <td></td>
                                     <td style="text-align: right;">{$totalTaxAmount}</td>
                                     <td style="text-align: right;">{$orderAmount}</td>
@@ -686,11 +857,11 @@ ORDER;
                             <table width="100%" style="border: none; padding: 10px 20px; text-align: center;">
                                 <tr style="border: none;">
                                     <td width="70%" style="border: none;"></td>
-                                    <td width="30%" style="font-weight: bold; text-align: left; font-size: 14px; border: none;">Signature:</td>
+                                    <td width="30%" style="font-weight: bold; text-align: left; font-size: 14px; border: none;">{$signature}:</td>
                                 </tr>
                                 <tr style="border: none;">
                                     <td width="70%" style="border: none;"></td>
-                                    <td width="30%" style="font-weight: bold; text-align: left; font-size: 14px; border: none;">Date:</td>
+                                    <td width="30%" style="font-weight: bold; text-align: left; font-size: 14px; border: none;">{$date}:</td>
                                 </tr>
                             </table>
                         </div>
@@ -810,7 +981,7 @@ ORDER;
             $userName = $value['REAL_NAME'];
             $address = $provinceName . $cityName . $countyName . $value['ADDRESS'];
             $mobile = $value['MOBILE'];
-            $orderAt = Date::convert($value['PAY_AT'] ?? $value['CREATED_AT'],'Y-m-d H:i:s');
+            $orderAt = Date::convert($value['CREATED_AT'],'Y-m-d H:i:s');
 
             // 总价
             $totalAmount = $value['BUY_NUMS'] * $value['REAL_PRICE'];
@@ -824,46 +995,64 @@ ORDER;
             // 订单详情
             $orderDetails .= <<<EOT
                 <tr>
-                    <td>{$value['SKU_CODE']}</td>
-                    <td>{$value['GOODS_TITLE']}</td>
+                    <td style="text-align: center;">{$value['SKU_CODE']}</td>
+                    <td style="text-align: center;">{$value['GOODS_TITLE']}</td>
                     <td style="text-align: right;">{$value['REAL_PRICE']}</td>
-                    <td>{$value['BUY_NUMS']}</td>
-                    <td style="text-align: right;">{$value['TAX_RATE']}</td>
+                    <td style="text-align: right;">{$value['BUY_NUMS']}</td>
+                    <td style="text-align: right;">{$value['TAX_RATE']}%</td>
                     <td style="text-align: right;">{$taxAmount}</td>
                     <td style="text-align: right;">{$totalAmount}</td> 
                 </tr>
 EOT;
         }
 
+        $memberCode = Yii::t('app', 'memberCode');
+        $memberName = Yii::t('app', 'memberName');
+        $memberAddress = Yii::t('app', 'memberAddress');
+        $memberPhone = Yii::t('app', 'memberPhone');
+        $orderCode = Yii::t('app', 'orderCode');
+        $orderDetail = Yii::t('app', 'orderDetail');
+        $productCode = Yii::t('app', 'productCode');
+        $productName = Yii::t('app', 'productName');
+        $productPrice = Yii::t('app', 'productPrice');
+        $quantity = Yii::t('app', 'qty');
+        $taxRate = Yii::t('app', 'taxRate');
+        $totalTax = Yii::t('app', 'totalTax');
+        $totalAmount = Yii::t('app', 'totalAmount');
+        $total = Yii::t('app', 'total');
+        $signature = Yii::t('app', 'signature');
+        $date = Yii::t('app', 'date');
+        $createAt = Yii::t('app', 'createAt');
+
         // 订单基本信息
         $orderBase = <<<ORDER
             <table border="1" style="table-layout: fixed; padding: 10px 20px;" width="100%">
                 <tr>
-                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Member Code</td>
+                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">{$memberCode}</td>
                     <td width="70%">{$userId}</td>
                 </tr>
                 <tr>
-                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Member Name</td>
+                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">{$memberName}</td>
                     <td width="70%">{$userName}</td>
                 </tr>
                 <tr>
-                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Member Address</td>
+                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">{$memberAddress}</td>
                     <td width="70%">{$address}</td>
                 </tr>
                 <tr>
-                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Member Phone</td>
+                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">{$memberPhone}</td>
                     <td width="70%">{$mobile}</td>
                 </tr>
                 <tr>
-                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Order Code</td>
+                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">{$orderCode}</td>
                     <td width="70%">{$orderSn}</td>
                 </tr>
                 <tr>
-                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Creation Time</td>
+                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">{$createAt}</td>
                     <td width="70%">{$orderAt}</td>
                 </tr>
                 <tr>
-                    <td class="bg" style="font-weight: bold; font-size: 14px; text-align: center;">Order detail</td>
+                    <td class="bg" style="font-weight: bold; font-size: 14px; text-align: center;">{$orderDetail}</td>
                     <td class="bg"></td>
                 </tr>
             </table>
@@ -882,14 +1071,14 @@ ORDER;
             <html lang="en">
             <head>
                 <meta charset="UTF-8" />
-                <title>Order detail</title>
+                <title>{$orderDetail}</title>
                 <style>
                     table {
                         border-collapse: collapse;
                     }
                     table td, table th {
                         border: 1px solid #ccc;
-                        padding: 5px 5px;
+                        /*padding: 5px 5px;*/
                         border-collapse: collapse;
                     }
                     /*td {*/
@@ -902,28 +1091,28 @@ ORDER;
             </head>
             <body>
                 <div class="content">
-                    <p style="text-align: center; font-weight: bold; font-size: 22px;"><b>Order detail</b><br></p>
+                    <p style="text-align: center; font-weight: bold; font-size: 22px;"><b>{$orderDetail}</b><br></p>
                     <div>
                         <div style="display: block; width: 100%;">
                             {$orderBase}
                             
-                            <table border="1" width="100%" style="padding: 10px 20px; text-align: center;">
+                            <table border="1" width="100%" style="padding: 10px 5px; text-align: center;">
                                 <tr>
-                                    <th width="15%" style="font-size: 14px; font-weight: bold; text-align: center;">Product Code</th>
-                                    <th width="25%" style="font-size: 14px; font-weight: bold; text-align: center;">Product Name</th>
-                                    <th width="15%" style="font-size: 14px; font-weight: bold; text-align: center;">Product Price</th>
-                                    <th width="10%" style="font-size: 14px; font-weight: bold; text-align: center;">Qty</th>
-                                    <th width="10%" style="font-size: 14px; font-weight: bold; text-align: center;">Tax Rate</th>
-                                    <th width="10%" style="font-size: 14px; font-weight: bold; text-align: center;">Tax</th>
-                                    <th width="15%" style="font-size: 14px; font-weight: bold; text-align: center;">Total Amount</th>
+                                    <th width="15%" style="font-size: 14px; font-weight: bold; text-align: center;">{$productCode}</th>
+                                    <th width="20%" style="font-size: 14px; font-weight: bold; text-align: center;">{$productName}</th>
+                                    <th width="15%" style="font-size: 14px; font-weight: bold; text-align: center;">{$productPrice}</th>
+                                    <th width="10%" style="font-size: 14px; font-weight: bold; text-align: center;">{$quantity}</th>
+                                    <th width="12%" style="font-size: 14px; font-weight: bold; text-align: center;">{$taxRate}</th>
+                                    <th width="13%" style="font-size: 14px; font-weight: bold; text-align: center;">{$totalTax}</th>
+                                    <th width="15%" style="font-size: 14px; font-weight: bold; text-align: center;">{$totalAmount}</th>
                                 </tr>
                                 {$orderDetails}
                                 <tr>
-                                    <td colspan="3">Total</td>
-                                    <td>{$orderNums}</td>
+                                    <td colspan="3">{$total}</td>
+                                    <td style="text-align: right;">{$orderNums}</td>
                                     <td></td>
-                                    <td>{$totalTaxAmount}</td>
-                                    <td>{$orderAmount}</td>
+                                    <td style="text-align: right;">{$totalTaxAmount}</td>
+                                    <td style="text-align: right;">{$orderAmount}</td>
                                 </tr>
                             </table>
                         </div>
@@ -932,11 +1121,11 @@ ORDER;
                             <table width="100%" style="border: none; padding: 10px 20px; text-align: center;">
                                 <tr style="border: none;">
                                     <td width="70%" style="border: none;"></td>
-                                    <td width="30%" style="font-weight: bold; text-align: left; font-size: 14px; border: none;">Signature:</td>
+                                    <td width="30%" style="font-weight: bold; text-align: left; font-size: 14px; border: none;">{$signature}:</td>
                                 </tr>
                                 <tr style="border: none;">
                                     <td width="70%" style="border: none;"></td>
-                                    <td width="30%" style="font-weight: bold; text-align: left; font-size: 14px; border: none;">Date:</td>
+                                    <td width="30%" style="font-weight: bold; text-align: left; font-size: 14px; border: none;">{$date}:</td>
                                 </tr>
                             </table>
                         </div>
@@ -1071,7 +1260,7 @@ ORDER;
         if (\Yii::$app->request->isPost) {
             $formModel = new BaApproachOrderForm();
             $formModel->scenario = 'userOrder';
-            $formModel->remark = '复销备注';
+            $formModel->remark = Yii::t('app', 'reconsumeRemark');
             $post = \Yii::$app->request->post();
             $post['type'] = DeclarationForm::TYPE_FX;
             if ($formModel->load($post, '') && $order = $formModel->add()) {
@@ -1161,12 +1350,23 @@ ORDER;
         return static::notice($data);
     }
 
+    /**
+     * @throws HttpException
+     */
+    public function actionBaDecOrderList() {
+        $version = \Yii::$app->request->get('v');
+
+        $data = $version == 2 ? $this->baDecOrderListV2() : $this->baDecOrderListV1();
+
+        return static::notice($data);
+    }
+
     /**
      * 我的BA报单
      * @return mixed
      * @throws HttpException
      */
-    public function actionBaDecOrderList() {
+    public function baDecOrderListV1() {
         $condition = ' DO.USER_ID=:USER_ID AND DO.IS_DEL=0';
         $params[':USER_ID'] = \Yii::$app->user->id;
 
@@ -1219,7 +1419,75 @@ ORDER;
             $value['TOTAL_AMOUNT'] = Tool::formatPrice($value['REAL_PRICE'] * $value['BUY_NUMS']);
         }
 
-        return static::notice($data);
+        return $data;
+    }
+
+    /**
+     * 我的BA报单
+     * @return mixed
+     * @throws HttpException
+     */
+    public function baDecOrderListV2() {
+        $condition = ' DO.USER_ID=:USER_ID AND DO.IS_DEL=0';
+        $params[':USER_ID'] = \Yii::$app->user->id;
+
+        $orderQuery = BaDecOrder::find()
+            ->alias('DO')
+            ->where($condition, $params)
+            ->select('DO.DEC_SN,DO.ORDER_SN,DO.CREATED_AT,O.STATUS,O.CONSIGNEE,O.MOBILE,O.PAY_TYPE,O.PAY_AT,O.ADDRESS,O.CITY_NAME,O.LGA_NAME,O.PROVINCE,U.USER_NAME USER_NAME,U.REAL_NAME REAL_NAME,RU.USER_NAME REC_USER_NAME,RU.REAL_NAME REC_REAL_NAME,CU.USER_NAME CON_USER_NAME,CU.REAL_NAME CON_REAL_NAME')
+            ->join('LEFT JOIN', BaUser::tableName() . ' AS U', 'DO.TO_USER_ID=U.ID')
+            ->join('LEFT JOIN', User::tableName() . ' AS RU', 'DO.REC_USER_ID=RU.ID')
+            ->join('LEFT JOIN', User::tableName() . ' AS CU', 'DO.CON_USER_ID=CU.ID')
+            ->join('LEFT JOIN', BaOrder::tableName() . ' AS O', 'O.SN=DO.ORDER_SN')
+            ->orderBy('DO.CREATED_AT DESC');
+
+        // 订单中间表只查询待支付和支付失败的订单
+        $params[':NOT_PAID'] = \Yii::$app->params['orderStatus']['notPaid']['value'];   // 待支付
+        $params[':FAIL_PAID'] = \Yii::$app->params['orderStatus']['failPaid']['value'];   // 支付失败
+        $orderStandardQuery = BaApproachDecOrder::find()
+            ->alias('DO')
+            ->where($condition . ' AND (O.STATUS = :NOT_PAID OR O.STATUS = :FAIL_PAID)', $params)
+            ->select('DO.DEC_SN,DO.ORDER_SN,DO.CREATED_AT,O.STATUS,O.CONSIGNEE,O.MOBILE,O.PAY_TYPE,O.PAY_AT,O.ADDRESS,O.CITY_NAME,O.LGA_NAME,O.PROVINCE,U.USER_NAME USER_NAME,U.REAL_NAME REAL_NAME,RU.USER_NAME REC_USER_NAME,RU.REAL_NAME REC_REAL_NAME,CU.USER_NAME CON_USER_NAME,CU.REAL_NAME CON_REAL_NAME')
+            ->join('LEFT JOIN', BaUser::tableName() . ' AS U', 'DO.TO_USER_ID=U.ID')
+            ->join('LEFT JOIN', User::tableName() . ' AS RU', 'DO.REC_USER_ID=RU.ID')
+            ->join('LEFT JOIN', User::tableName() . ' AS CU', 'DO.CON_USER_ID=CU.ID')
+            ->join('LEFT JOIN', BaApproachOrder::tableName() . ' AS O', 'O.SN=DO.ORDER_SN')
+            ->orderBy('DO.CREATED_AT DESC');
+
+        $queryAll = $orderQuery->union($orderStandardQuery, true);
+        $query = (new Query())->from(['Q' => $queryAll])->select('Q.*')->distinct()->orderBy(['CREATED_AT' => SORT_DESC]);
+
+        $totalCount = $query->count();
+        $pagination = new Pagination(['totalCount' => $totalCount, 'pageSize' => \Yii::$app->request->get('pageSize')]);
+        $lists = $query->offset($pagination->offset)->limit($pagination->limit)->all();
+
+        $data = [
+            'list' => $lists,
+            'currentPage'=>$pagination->page,
+            'totalPages'=>$pagination->pageCount,
+            'startNum' => $pagination->page * $pagination->pageSize + 1,
+            'totalCount' => $pagination->totalCount,
+            'pageSize' => $pagination->pageSize,
+        ];
+
+        foreach ($data['list'] as &$value) {
+            if (($value['PAY_TYPE'] == 'pay_stack') && in_array($value['STATUS'], [\Yii::$app->params['orderStatus']['notPaid']['value'], \Yii::$app->params['orderStatus']['failPaid']['value']])) {
+                // 订单中间表
+                $orderGoods = BaApproachOrderGoods::findAllAsArray('ORDER_SN=:ORDER_SN', [':ORDER_SN' => $value['ORDER_SN']], 'SKU_CODE,GOODS_TITLE,BUY_NUMS,TAX_RATE,REAL_PV,REAL_PRICE');
+            } else {
+                // 订单表
+                $orderGoods = BaOrderGoods::findAllAsArray('ORDER_SN=:ORDER_SN', [':ORDER_SN' => $value['ORDER_SN']], 'SKU_CODE,GOODS_TITLE,BUY_NUMS,TAX_RATE,REAL_PV,REAL_PRICE');
+            }
+            $value['hasChildren'] = $orderGoods;
+            $value['STATUS'] = \Yii::$app->params['orderStatus'][$value['STATUS']]['label'] ?? '';
+            $value['CREATED_AT'] = $value['CREATED_AT'] ? Date::convert($value['CREATED_AT'],'Y-m-d H:i:s') : '';
+            $value['PAY_TYPE'] = array_column(ShopGoods::SALE_TYPE, NULL, 'label')[$value['PAY_TYPE']]['name'] ?? '';
+            $value['FULL_ADDRESS'] = trim(implode(', ', [$value['ADDRESS'], $value['CITY_NAME'], $value['LGA_NAME'], Region::getCnName($value['PROVINCE'])]), ', ');
+
+            unset($value['ADDRESS'], $value['CITY_NAME'], $value['LGA_NAME'], $value['PROVINCE']);
+        }
+
+        return $data;
     }
 
     /**
@@ -1297,46 +1565,64 @@ ORDER;
             // 订单详情
             $orderDetails .= <<<EOT
                 <tr>
-                    <td>{$value['SKU_CODE']}</td>
-                    <td>{$value['GOODS_TITLE']}</td>
+                    <td style="text-align: center;">{$value['SKU_CODE']}</td>
+                    <td style="text-align: center;">{$value['GOODS_TITLE']}</td>
                     <td style="text-align: right;">{$value['REAL_PRICE']}</td>
-                    <td>{$value['BUY_NUMS']}</td>
-                    <td style="text-align: right;">{$value['TAX_RATE']}</td>
+                    <td style="text-align: right;">{$value['BUY_NUMS']}</td>
+                    <td style="text-align: right;">{$value['TAX_RATE']}%</td>
                     <td style="text-align: right;">{$taxAmount}</td>
                     <td style="text-align: right;">{$totalAmount}</td> 
                 </tr>
 EOT;
         }
 
+        $memberCode = Yii::t('app', 'memberCode');
+        $memberName = Yii::t('app', 'memberName');
+        $memberAddress = Yii::t('app', 'memberAddress');
+        $memberPhone = Yii::t('app', 'memberPhone');
+        $orderCode = Yii::t('app', 'orderCode');
+        $orderDetail = Yii::t('app', 'orderDetail');
+        $productCode = Yii::t('app', 'productCode');
+        $productName = Yii::t('app', 'productName');
+        $productPrice = Yii::t('app', 'productPrice');
+        $quantity = Yii::t('app', 'qty');
+        $taxRate = Yii::t('app', 'taxRate');
+        $totalTax = Yii::t('app', 'totalTax');
+        $totalAmount = Yii::t('app', 'totalAmount');
+        $total = Yii::t('app', 'total');
+        $signature = Yii::t('app', 'signature');
+        $date = Yii::t('app', 'date');
+        $createAt = Yii::t('app', 'createAt');
+
         // 订单基本信息
         $orderBase = <<<ORDER
             <table border="1" style="table-layout: fixed; padding: 10px 20px;" width="100%">
                 <tr>
-                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Member Code</td>
+                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">{$memberCode}</td>
                     <td width="70%">{$userId}</td>
                 </tr>
                 <tr>
-                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Member Name</td>
+                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">{$memberName}</td>
                     <td width="70%">{$userName}</td>
                 </tr>
                 <tr>
-                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Member Address</td>
+                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">{$memberAddress}</td>
                     <td width="70%">{$address}</td>
                 </tr>
                 <tr>
-                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Member Phone</td>
+                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">{$memberPhone}</td>
                     <td width="70%">{$mobile}</td>
                 </tr>
                 <tr>
-                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Order Code</td>
+                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">{$orderCode}</td>
                     <td width="70%">{$orderSn}</td>
                 </tr>
                 <tr>
-                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">Creation Time</td>
+                    <td width="30%" style="font-weight: bold; text-align: center; font-size: 14px;">{$createAt}</td>
                     <td width="70%">{$orderAt}</td>
                 </tr>
                 <tr>
-                    <td class="bg" style="font-weight: bold; font-size: 14px; text-align: center;">Order detail</td>
+                    <td class="bg" style="font-weight: bold; font-size: 14px; text-align: center;">{$orderDetail}</td>
                     <td class="bg"></td>
                 </tr>
             </table>
@@ -1355,14 +1641,14 @@ ORDER;
             <html lang="en">
             <head>
                 <meta charset="UTF-8" />
-                <title>Order detail</title>
+                <title>{$orderDetail}</title>
                 <style>
                     table {
                         border-collapse: collapse;
                     }
                     table td, table th {
                         border: 1px solid #ccc;
-                        padding: 5px 5px;
+                        /*padding: 5px 5px;*/
                         border-collapse: collapse;
                     }
                     /*td {*/
@@ -1375,28 +1661,28 @@ ORDER;
             </head>
             <body>
                 <div class="content">
-                    <p style="text-align: center; font-weight: bold; font-size: 22px;"><b>Order detail</b><br></p>
+                    <p style="text-align: center; font-weight: bold; font-size: 22px;"><b>{$orderDetail}</b><br></p>
                     <div>
                         <div style="display: block; width: 100%;">
                             {$orderBase}
                             
-                            <table border="1" width="100%" style="padding: 10px 20px; text-align: center;">
+                            <table border="1" width="100%" style="padding: 10px 5px; text-align: center;">
                                 <tr>
-                                    <th width="15%" style="font-size: 14px; font-weight: bold; text-align: center;">Product Code</th>
-                                    <th width="25%" style="font-size: 14px; font-weight: bold; text-align: center;">Product Name</th>
-                                    <th width="15%" style="font-size: 14px; font-weight: bold; text-align: center;">Product Price</th>
-                                    <th width="8%" style="font-size: 14px; font-weight: bold; text-align: center;">Qty</th>
-                                    <th width="10%" style="font-size: 14px; font-weight: bold; text-align: center;">Tax Rate</th>
-                                    <th width="12%" style="font-size: 14px; font-weight: bold; text-align: center;">Tax</th>
-                                    <th width="15%" style="font-size: 14px; font-weight: bold; text-align: center;">Total Amount</th>
+                                    <th width="15%" style="font-size: 14px; font-weight: bold; text-align: center;">{$productCode}</th>
+                                    <th width="20%" style="font-size: 14px; font-weight: bold; text-align: center;">{$productName}</th>
+                                    <th width="15%" style="font-size: 14px; font-weight: bold; text-align: center;">{$productPrice}</th>
+                                    <th width="10%" style="font-size: 14px; font-weight: bold; text-align: center;">{$quantity}</th>
+                                    <th width="12%" style="font-size: 14px; font-weight: bold; text-align: center;">{$taxRate}</th>
+                                    <th width="13%" style="font-size: 14px; font-weight: bold; text-align: center;">{$totalTax}</th>
+                                    <th width="15%" style="font-size: 14px; font-weight: bold; text-align: center;">{$totalAmount}</th>
                                 </tr>
                                 {$orderDetails}
                                 <tr>
-                                    <td colspan="3">Total</td>
-                                    <td>{$orderNums}</td>
+                                    <td colspan="3">{$total}</td>
+                                    <td style="text-align: right;">{$orderNums}</td>
                                     <td></td>
-                                    <td>{$totalTaxAmount}</td>
-                                    <td>{$orderAmount}</td>
+                                    <td style="text-align: right;">{$totalTaxAmount}</td>
+                                    <td style="text-align: right;">{$orderAmount}</td>
                                 </tr>
                             </table>
                         </div>
@@ -1405,11 +1691,11 @@ ORDER;
                             <table width="100%" style="border: none; padding: 10px 20px; text-align: center;">
                                 <tr style="border: none;">
                                     <td width="70%" style="border: none;"></td>
-                                    <td width="30%" style="font-weight: bold; text-align: left; font-size: 14px; border: none;">Signature:</td>
+                                    <td width="30%" style="font-weight: bold; text-align: left; font-size: 14px; border: none;">{$signature}:</td>
                                 </tr>
                                 <tr style="border: none;">
                                     <td width="70%" style="border: none;"></td>
-                                    <td width="30%" style="font-weight: bold; text-align: left; font-size: 14px; border: none;">Date:</td>
+                                    <td width="30%" style="font-weight: bold; text-align: left; font-size: 14px; border: none;">{$date}:</td>
                                 </tr>
                             </table>
                         </div>

+ 51 - 6
frontendApi/modules/v1/controllers/SiteController.php

@@ -16,6 +16,7 @@ use common\models\ArticleCategory;
 use common\models\BaUser;
 use common\models\DecOrder;
 use common\models\DecRole;
+use common\models\Period;
 use Yii;
 use frontendApi\modules\v1\models\User;
 
@@ -109,24 +110,68 @@ class SiteController extends BaseController
         $decLevels = Cache::getDecLevelConfig();
         // 聘级
         $empLevels = Cache::getEmpLevelConfig();
-        // 菜单
-        $menu = require Yii::getAlias('@frontendApi/config/menu.php');
-        // 获取全部文章分类
-        $allArticleCategory = ArticleCategory::getAllCategory();
-        foreach($allArticleCategory as $category){
-            $menu['article']['child'][] = ['name'=>$category['CATE_NAME'], 'class'=>'', 'icon'=>'', 'controller'=>'article', 'action'=>'list', 'routePath'=>'article/list/'.$category['ID'], 'show'=>1,];
+        // 皇冠星级
+        $crownLevels = Cache::getStarCrownLevelConfig();
+        // 根据版本标识区分菜单
+        $version = Yii::$app->request->get('version', '');
+        if ($version == 'v2') {
+            // 菜单
+            $menu = require Yii::getAlias('@frontendApi/config/menuV2.php');
+        } else {
+            // 菜单
+            $menu = require Yii::getAlias('@frontendApi/config/menu.php');
+            // 获取全部文章分类
+            $allArticleCategory = ArticleCategory::getAllCategory();
+            foreach($allArticleCategory as $category){
+                $menu['article']['child'][] = ['name'=>$category['CATE_NAME'], 'class'=>'', 'icon'=>'', 'controller'=>'article', 'action'=>'list', 'routePath'=>'article/list/'.$category['ID'], 'show'=>1,];
+            }
         }
         $menu = $this->_childMenu($menu);
+
         // 时间差
         $daysDiff = Yii::$app->params['daysDiff'];
         // 钱包
         $shopWalletType = Yii::$app->params['shopWalletType'];
 
+        //期数显示
+        $periodNum = Period::instance()->getNowPeriodNum();
+        $curYM = Period::find()->select("CALC_YEAR,CALC_MONTH")->where('PERIOD_NUM=:PERIOD_NUM', [':PERIOD_NUM' => $periodNum])->asArray()->one();
+        $plist = Period::find()->select("PERIOD_NUM")->where('CALC_YEAR=:CALC_YEAR AND CALC_MONTH=:CALC_MONTH', [':CALC_YEAR' => $curYM['CALC_YEAR'],':CALC_MONTH'=>$curYM['CALC_MONTH']])->orderBy('PERIOD_NUM ASC')->asArray()->all();
+        $weeks = count($plist);
+        $wkrd = '';
+        foreach ($plist as $k=>$v){
+            if($v['PERIOD_NUM']==$periodNum){
+                $wkrd =$k+1;break;
+            }
+        }
+        if($wkrd==1){
+            $wkrd.='st';
+        }else{
+            $wkrd.='nd';
+        }
+
+        $monthArray = [
+            1 => 'Jan',
+            2 => 'Feb',
+            3 => 'Mar',
+            4 => 'Apr',
+            5 => 'May',
+            6 => 'Jun',
+            7 => 'Jul',
+            8 => 'Aug',
+            9 => 'Sep',
+            10 => 'Oct',
+            11 => 'Nov',
+            12 => 'Dec',
+        ];
+
         return [
             'decLevels' => $decLevels,
             'empLevels' => $empLevels,
+            'crownLevels' => $crownLevels,
             'menu' => $menu,
             'daysDiff' => $daysDiff,
+            'periodNum' => $periodNum . ',' . $wkrd . ' PC of ' . $monthArray[$curYM['CALC_MONTH']],
             'shopWalletType' => $shopWalletType,
             'whetherBA' => false,
         ];

+ 63 - 31
frontendApi/modules/v1/controllers/UserController.php

@@ -35,10 +35,13 @@ use common\models\ShopGoods;
 use common\models\UpgradeType;
 use common\models\User;
 use common\models\UserBind;
+use common\models\UserBonus;
 use common\models\UserInfo;
 use common\models\BaUserInfo;
 use common\models\UserNetwork;
 use common\models\forms\DeclarationUpgradeForm;
+use common\models\UserWallet;
+use Yii;
 use yii\db\Exception;
 use yii\web\UploadedFile;
 
@@ -71,12 +74,12 @@ class UserController extends BaseController {
             $post = \Yii::$app->request->post();
             $form->scenario = 'modifyProfile';
             if($form->load($post, '') && $result = $form->modifyProfile()){
-                return static::notice('Personal data modified successfully');//个人资料修改成功
+                return static::notice(Yii::t('app', 'personalDataModifiedSuccessfully'), 400);
             } else {
                 return static::notice(Form::formatErrorsForApi($form->getErrors()), 400);
             }
         }
-        return static::notice('Illegal request', 400); // 非法请求
+        return static::notice(Yii::t('app', 'illegalRequest'), 400);
     }
 
     /**
@@ -88,12 +91,12 @@ class UserController extends BaseController {
             $form->scenario = 'modifyPassword';
             $post = \Yii::$app->request->post();
             if($form->load($post, '') && $result = $form->modifyPassword()){
-                return static::notice('Password modified successfully'); // 密码修改成功
+                return static::notice(Yii::t('app', 'passwordModifiedSuccessfully'), 400);
             } else {
                 return static::notice(Form::formatErrorsForApi($form->getErrors()), 400);
             }
         }
-        return static::notice('Illegal request', 400); // 非法请求
+        return static::notice(Yii::t('app', 'illegalRequest'), 400);
     }
 
     /**
@@ -106,12 +109,12 @@ class UserController extends BaseController {
             $post = \Yii::$app->request->post();
             $form->userId = \Yii::$app->user->id;
             if($form->load($post, '') && $result = $form->modifyPasswordPay()){
-                return static::notice('支付密码修改成功');
+                return static::notice(Yii::t('app', 'passwordModifiedSuccessfully'));
             } else {
                 return static::notice(Form::formatErrorsForApi($form->getErrors()), 400);
             }
         }
-        return static::notice('Illegal request', 400); // 非法请求
+        return static::notice(Yii::t('app', 'illegalRequest'), 400);
     }
 
     /**
@@ -143,7 +146,7 @@ class UserController extends BaseController {
             //$formModel->token = \Yii::$app->request->post('uploadToken');
             $formModel->token = \Yii::$app->request->request('uploadToken');
             if ($formModel->file && $formModel->upload()) {
-                return static::notice('Successful');
+                return static::notice(Yii::t('app', 'successfully'));
             } else {
                 return static::notice(Form::formatErrorsForApi($formModel->getErrors()), 400);
             }
@@ -194,7 +197,7 @@ class UserController extends BaseController {
     public function actionBindEdit(){
         $id = \Yii::$app->request->get('id');
         if(\Yii::$app->request->isPost) {
-            return parent::edit(UserBindForm::class, '修改主点位成功', 'frontEdit', ['frontEdit'], null, function($form, $result){
+            return parent::edit(UserBindForm::class, Yii::t('app', 'successfully'), 'frontEdit', ['frontEdit'], null, function($form, $result){
                 //log
             });
         }
@@ -222,12 +225,12 @@ class UserController extends BaseController {
         ->one();
         $isOpen = !empty($isSwitchUpgrade) && isset($isSwitchUpgrade['VALUE']) ? $isSwitchUpgrade['VALUE'] : 0;
         if ($isOpen < 1) {
-            return static::notice('The function is not available',400); // 功能暂未开放
+            return static::notice(Yii::t('app', 'theFunctionIsNotAvailable'), 400);
         }
         $userNumber = \Yii::$app->request->request('userName');
         $baseInfo = Info::baseInfoZhByUserName($userNumber);
         if ($baseInfo['STATUS'] != 1) {
-            return static::notice('Inactive user. Please contact customer service.',400);// 非激活用户,请联系客服
+            return static::notice(Yii::t('app', 'inactiveUser'), 400);
         }
         // 1. 如果是最高级别,则只显示用户基本信息
         // 2. 如果不是最高级别,如果用户累计报单数据是0, 或者用户累计报单业绩不符合级别信息,则提示 请联系客服核对业绩
@@ -272,10 +275,10 @@ class UserController extends BaseController {
             $nextLevelPerf = DeclarationLevel::getNextDecPref($levelPerf)['PERF'];
             // 如果总和超过了下一级业绩
             if ($userDecPvSum >= $nextLevelPerf) {
-                return static::notice('Please contact customer service to check performance.',400);// 请联系客服人员核对业绩
+                return static::notice(Yii::t('app', 'checkPerformance'), 400);
             }
             $type = $isObserve ? 1 : 2;
-            $userInfo['UPGRADE_FUNC'] = $isObserve ? 'filling up of a deficit' : 'full payment';// 升级方式
+            $userInfo['UPGRADE_FUNC'] = $isObserve ? Yii::t('app', 'fillingUpOfADeficit') : Yii::t('app', 'fullPayment');
             $upgradeType = UpgradeType::getOneByType($type);
             // 如果用户不是最大级别,则需要获取是否观察期,算出PV是否有问题,应该补多少,
             $userInfo['UPGRADE_TYPE'] = $upgradeType;
@@ -300,7 +303,7 @@ class UserController extends BaseController {
         ->one();
         $isOpen = !empty($isSwitchUpgrade) && isset($isSwitchUpgrade['VALUE']) ? $isSwitchUpgrade['VALUE'] : 0;
         if ($isOpen < 1) {
-            return static::notice('功能暂未开放',400);
+            return static::notice(Yii::t('app', 'theFunctionIsNotAvailable'), 400);
         }
         // 开始升级
         if (\Yii::$app->request->isPost) {
@@ -348,6 +351,13 @@ class UserController extends BaseController {
             $goods['TAX'] = Tool::calculateTax($goods['SELL_PRICE'], $goods['TAX_RATE']);
         }
 
+        // 会员账户
+        $userBalance = ['cash' => 0];
+        $userCash = UserWallet::findOneAsArray(['USER_ID' => \Yii::$app->user->id]);
+        if ($userCash) {
+            $userBalance['cash'] = $userCash['CASH'];
+        }
+
         return static::notice([
             'allDecPackage' => $allDecPackage,
             'allGoods' => $allGoods,
@@ -355,6 +365,7 @@ class UserController extends BaseController {
             'payList' => ShopGoods::payTypes(),
             'sellType' => ShopGoods::CATEGORY_TYPE,
             'categoryType' => ShopGoods::CATEGORY_TYPE[0]['id'],
+            'userBalance' => $userBalance,
         ]);
     }
 
@@ -393,10 +404,10 @@ class UserController extends BaseController {
                 $insertUserName = strtoupper($post['insertUserName']);
                 $getRedisUserName = $redis->get('key_' . $insertUserName);
                 if (!$getRedisUserName){
-                    return static::notice('Membership number expired',400);//会员编号过期
+                    return static::notice(Yii::t('app', 'memberNumberExpired'), 400);
                 }
                 if ($insertUserName != $getRedisUserName){
-                    return static::notice('Member number does not conform to',400);//会员编号不符合
+                    return static::notice(Yii::t('app', 'memberNumberDoesNotConformTo'), 400);
                 }
             }else{ // BA升级,需要判断BA用户是否存在
                 $insertUserName = $post['insertUserName'];
@@ -439,11 +450,18 @@ class UserController extends BaseController {
         // 所有开户行
         $allOpenBank = OpenBank::find()->where('STATUS=1')->orderBy('LIST_ORDER ASC')->asArray()->all();
         if (!$userName) {
-            return static::notice('Failed to generate member number', 400);//会员编号生成失败
+            return static::notice(Yii::t('app', 'failedToGenerateMemberNumber'), 400);
         }
         //随机码保存在redis中方便进行比对
         $redis->setex('key_'.$userName , 3600 , $userName);
 
+        // 会员账户
+        $userBalance = ['cash' => 0];
+        $userCash = UserWallet::findOneAsArray(['USER_ID' => \Yii::$app->user->id]);
+        if ($userCash) {
+            $userBalance['cash'] = $userCash['CASH'];
+        }
+
         return static::notice([
             'allDecPackage' => $allDecPackage,
             'allGoods' => $allGoods,
@@ -452,7 +470,8 @@ class UserController extends BaseController {
             'payList' => ShopGoods::payTypes(),
             'sellType' => ShopGoods::CATEGORY_TYPE,
             'categoryType' => ShopGoods::CATEGORY_TYPE[0]['id'],
-            ]);
+            'userBalance' => $userBalance,
+        ]);
     }
 
     /**
@@ -496,18 +515,19 @@ class UserController extends BaseController {
         if($user){
             $userInfo['REAL_NAME'] = $user['REAL_NAME'];
             $allChildUser = UserNetwork::getFirstFloorChildren($userId);
-            $isLocation = [1 => 'Left-', 2 => 'Right-'];
+            $isLocation = [1 => 'Left-Null', 2 => 'Right-Null'];
             if($allChildUser) {
                 foreach ($allChildUser as $child) {
                     if ($child['RELATIVE_LOCATION']<'3'){
-                        $isLocation[$child['RELATIVE_LOCATION']].= 'Full';
+//                        $isLocation[$child['RELATIVE_LOCATION']].= 'Full';
+                        $isLocation[$child['RELATIVE_LOCATION']] = str_replace('Null', 'Full', $isLocation[$child['RELATIVE_LOCATION']]);
                     }
                 }
             }
             $userInfo['isLocation'] = '('.implode(',',$isLocation).')';
             return static::notice($userInfo);
         }else{
-            return static::notice('Member number does not exist', 400);//会员编号不存在
+            return static::notice(Yii::t('app', 'memberNumberDoesNotExist'), 400);
         }
     }
 
@@ -544,7 +564,7 @@ class UserController extends BaseController {
             ];
             return static::notice($arr);
         }else{
-            return static::notice('Repeat sales Member No. does not exist', 400);//复消会员编号不存在
+            return static::notice(Yii::t('app', 'repeatSalesMemberNoDoesNotExist'), 400);
         }
     }
 
@@ -579,13 +599,13 @@ class UserController extends BaseController {
             $post = \Yii::$app->request->post();
             $form->scenario = 'modifyProfile';
             if ($form->load($post, '') && $result = $form->modifyProfile()){
-                return static::notice('Personal data modified successfully');//个人资料修改成功
+                return static::notice(Yii::t('app', 'personalDataModifiedSuccessfully'), 400);
             } else {
                 return static::notice(Form::formatErrorsForApi($form->getErrors()), 400);
             }
         }
 
-        return static::notice('Illegal request', 400); // 非法请求
+        return static::notice(Yii::t('app', 'illegalRequest'), 400);
     }
 
     /**
@@ -597,12 +617,12 @@ class UserController extends BaseController {
             $form->scenario = 'modifyPassword';
             $post = \Yii::$app->request->post();
             if($form->load($post, '') && $result = $form->modifyPassword()){
-                return static::notice('Password modified successfully');//密码修改成功
+                return static::notice(Yii::t('app', 'passwordModifiedSuccessfully'), 400);
             } else {
                 return static::notice(Form::formatErrorsForApi($form->getErrors()), 400);
             }
         }
-        return static::notice('Illegal request', 400); // 非法请求
+        return static::notice(Yii::t('app', 'illegalRequest'), 400);
     }
 
     /**
@@ -615,12 +635,12 @@ class UserController extends BaseController {
             $post = \Yii::$app->request->post();
             $form->userId = \Yii::$app->user->id;
             if($form->load($post, '') && $result = $form->modifyPasswordPay()){
-                return static::notice('Payment password modified successfully');  // 支付密码修改成功
+                return static::notice(Yii::t('app', 'passwordModifiedSuccessfully'), 400);
             } else {
                 return static::notice(Form::formatErrorsForApi($form->getErrors()), 400);
             }
         }
-        return static::notice('Illegal request', 400); // 非法请求
+        return static::notice(Yii::t('app', 'illegalRequest'), 400);
     }
     
     /**
@@ -657,10 +677,10 @@ class UserController extends BaseController {
             $insertUserName = strtoupper($post['insertUserName']);
             $getRedisUserName = $redis->get('key_' . $insertUserName);
             if (!$getRedisUserName){
-                return static::notice('Membership number expired',400); // 会员编号过期
+                return static::notice(Yii::t('app', 'memberNumberExpired'), 400);
             }
             if ($insertUserName != $getRedisUserName){
-                return static::notice('Member number does not conform to',400); // 会员编号不符合
+                return static::notice(Yii::t('app', 'memberNumberDoesNotConformTo'), 400);
             }
 
             $post['insertUserName'] = $insertUserName;
@@ -676,10 +696,21 @@ class UserController extends BaseController {
         // 只查询普通商品
         $allGoods = ShopGoods::find()->where("ID='375895243322691584'")->orderBy('SORT ASC')->asArray()->all();
         if (!$userName) {
-            return static::notice('Failed to generate member number', 400); // 会员编号生成失败
+            return static::notice(Yii::t('app', 'failedToGenerateMemberNumber'), 400);
         }
         //随机码保存在redis中方便进行比对
         $redis->setex('key_'.$userName , 3600 , $userName);
+        // 会员账户
+        $userBalance = [
+            'cash' => 0,
+//            'tourism_points' => 0,
+//            'garage_points' => 0,
+//            'villa_points' => 0,
+        ];
+        $userCash = UserWallet::findOneAsArray(['USER_ID' => \Yii::$app->user->id]);
+        if ($userCash) {
+            $userBalance['cash'] = $userCash['CASH'];
+        }
 
         return static::notice([
             'allGoods' => $allGoods,
@@ -687,6 +718,7 @@ class UserController extends BaseController {
             'payList' => ShopGoods::payTypes(),
             'sellType' => ShopGoods::CATEGORY_TYPE,
             'categoryType' => ShopGoods::CATEGORY_TYPE[0]['id'],
+            'userBalance' => $userBalance,
         ]);
     }
 
@@ -709,7 +741,7 @@ class UserController extends BaseController {
             $userInfo['REC_USER_NAME'] = $rec_user['USER_NAME'];
             return static::notice($userInfo);
         }else{
-            return static::notice('Member number does not exist', 400);//会员编号不存在
+            return static::notice(Yii::t('app', 'memberNumberDoesNotExist'), 400);
         }
     }
 }

+ 23 - 22
frontendApi/modules/v1/models/LoginForm.php

@@ -23,6 +23,7 @@ class LoginForm extends Model
     public $password;
     public $verifyCode;
     public $rememberMe = true;
+    public $version;
 
     private $_user;
     private $_userInfo;
@@ -133,33 +134,33 @@ class LoginForm extends Model
         try{
             $this->getUser();
             if(!$this->_user){
-                throw new Exception('The account does not exist'); // 账号不存在
+                throw new Exception(Yii::t('app', 'accountDoesNotExist'));
             }
 
             if (!$this->_user->validatePassword($this->password)) {
-                $this->_updateFailTimes($transaction,'The member name or password is incorrect'); // 用户名或密码错误
-                throw new Exception('The member name or password is incorrect'); // 用户名或密码错误
+                $this->_updateFailTimes($transaction,Yii::t('app', 'memberNameOrPasswordIncorrect'));
+                throw new Exception(Yii::t('app', 'memberNameOrPasswordIncorrect'));
             }
             // 找到会员的基本信息来判断其是否可登录
             if(!$this->_user['ALLOW_LOGIN']){
-                $this->_updateFailTimes($transaction,'Abnormal member code'); // 会员编号异常
-                throw new Exception('Abnormal member code'); // 会员编号异常
+                $this->_updateFailTimes($transaction,Yii::t('app', 'abnormalMemberCode'));
+                throw new Exception(Yii::t('app', 'abnormalMemberCode'));
             }
             if($this->_user['STATUS'] == Yii::$app->params['userStatus'][0]['value']){
-                $this->_updateFailTimes($transaction,'Member not activated'); // 会员未激活
-                throw new Exception('Member not activated'); // 会员未激活
+                $this->_updateFailTimes($transaction,Yii::t('app', 'memberNotActivated'));
+                throw new Exception(Yii::t('app', 'memberNotActivated'));
             } elseif($this->_user['STATUS'] == Yii::$app->params['userStatus'][2]['value']){
-                $this->_updateFailTimes($transaction,'The member has been cancelled'); // 会员已被注销
-                throw new Exception('The member has been cancelled'); // 会员已被注销
+                $this->_updateFailTimes($transaction,Yii::t('app', 'memberHasBeenCancelled'));
+                throw new Exception(Yii::t('app', 'memberHasBeenCancelled'));
             } elseif($this->_user['STATUS'] == Yii::$app->params['userStatus'][3]['value']){
-                $this->_updateFailTimes($transaction,'The member has been blacklisted'); // 会员已被列入黑名单
-                throw new Exception('The member has been blacklisted'); // 会员已被列入黑名单
+                $this->_updateFailTimes($transaction,Yii::t('app', 'memberHasBeenBlacklisted'));
+                throw new Exception(Yii::t('app', 'memberHasBeenBlacklisted'));
             } elseif($this->_user['STATUS'] == Yii::$app->params['userStatus'][9]['value']){
-                $this->_updateFailTimes($transaction,'The member has been permanently suspended'); // 会员已被永久关停
-                throw new Exception('The member has been permanently suspended'); // 会员已被永久关停
+                $this->_updateFailTimes($transaction,Yii::t('app', 'memberHasBeenPermanentlySuspended'));
+                throw new Exception(Yii::t('app', 'memberHasBeenPermanentlySuspended'));
             } elseif($this->_user['PART_FUNC_CLOSED'] == 1){
-                $this->_updateFailTimes($transaction,'Member part of the function is closed, unable to log in.'); // 会员部分功能关闭,无法登录
-                throw new Exception('Member part of the function is closed, unable to log in.'); // 会员部分功能关闭,无法登录
+                $this->_updateFailTimes($transaction,Yii::t('app', 'memberPartOfFunctionClosed'));
+                throw new Exception(Yii::t('app', 'memberPartOfFunctionClosed'));
             } elseif($this->_user['IS_MODIFY_PASSWORD'] == 1){
                 throw new Exception(self::ERROR_IS_MODIFY_PASSWORD);
             }
@@ -179,13 +180,13 @@ class LoginForm extends Model
 
                 if (!$this->_whetherBA) {
                     if (!User::updateAll($update, 'ID=:ID', ['ID' => $this->_user['ID']])) {
-                        $this->_updateFailTimes($transaction, 'Member APP device information update failed'); // 会员APP设备信息更新失败
-                        throw new Exception('Member APP device information update failed'); // 会员APP设备信息更新失败
+                        $this->_updateFailTimes($transaction, Yii::t('app', 'memberAppDeviceInformationUpdateFailed'));
+                        throw new Exception(Yii::t('app', 'memberAppDeviceInformationUpdateFailed'));
                     }
                 } else {
                     if (!BaUser::updateAll($update, 'ID=:ID', ['ID' => $this->_user['ID']])) {
-                        $this->_updateFailTimes($transaction, 'Member APP device information update failed'); // 会员APP设备信息更新失败
-                        throw new Exception('Member APP device information update failed'); // 会员APP设备信息更新失败
+                        $this->_updateFailTimes($transaction, Yii::t('app', 'memberAppDeviceInformationUpdateFailed'));
+                        throw new Exception(Yii::t('app', 'memberAppDeviceInformationUpdateFailed'));
                     }
                 }
             }
@@ -194,7 +195,7 @@ class LoginForm extends Model
             $this->_updateSuccessTimes();
             $transaction->commit();
 
-            UserLoginLogger::success($this->_userInfo);
+            UserLoginLogger::success($this->_userInfo, $this->version);
 
             // 把用户的登录时间存在操作时间里
             Yii::$app->tokenRedis->hset('user:timeOut', $this->_userInfo['USER_ID'], time());
@@ -206,8 +207,8 @@ class LoginForm extends Model
             }
         }catch(\Exception $e){
             $transaction->rollBack();
-            $this->setError($e->getFile() . '  ' . $e->getLine() . '  ' . $e->getMessage());
-            return false;
+//            $this->setError($e->getFile() . '  ' . $e->getLine() . '  ' . $e->getMessage());
+            throw new Exception($e->getMessage());
         }
     }
 

+ 4 - 0
frontendEle/src/views/layout/layout.vue

@@ -15,6 +15,9 @@
       </span>
             <el-dropdown-menu slot="dropdown">
               <el-dropdown-item icon="el-icon-user" @click.native="onGo('/user/index')">Personal Information</el-dropdown-item><!--个人资料-->
+              <el-dropdown-item icon="el-icon-s-promotion">
+                <a target="_blank" :href="visitWebsite">New Member Management System</a>
+              </el-dropdown-item><!--新版-->
               <!--<el-dropdown-item icon="el-icon-message" @click.native="onGo('/message/list')">站内信</el-dropdown-item>-->
 <!--              <el-dropdown-item icon="el-icon-setting" @click.native="onGo('/config/base')">个人设置</el-dropdown-item>-->
               <el-dropdown-item icon="el-icon-switch-button" @click.native="onLogout">Log Out</el-dropdown-item><!--安全退出-->
@@ -176,6 +179,7 @@ export default {
       tool: tool,
       siteTitle: '',
       whetherBA: baseInfo.whetherBA(),
+      visitWebsite: 'http://ngds.elken.com:8010', // test: 'http://16.163.228.151:8037',
     }
   },
   computed: {

+ 15 - 0
node_modules/.package-lock.json

@@ -0,0 +1,15 @@
+{
+  "name": "ngds",
+  "lockfileVersion": 2,
+  "requires": true,
+  "packages": {
+    "node_modules/moment": {
+      "version": "2.29.4",
+      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
+      "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
+      "engines": {
+        "node": "*"
+      }
+    }
+  }
+}

+ 988 - 0
node_modules/moment/CHANGELOG.md

@@ -0,0 +1,988 @@
+Changelog
+=========
+
+### 2.29.4
+
+* Release Jul 6, 2022
+  * [#6015](https://github.com/moment/moment/pull/6015) [bugfix] Fix ReDoS in preprocessRFC2822 regex
+
+### 2.29.3 [Full changelog](https://gist.github.com/ichernev/edebd440f49adcaec72e5e77b791d8be)
+
+* Release Apr 17, 2022
+  * [#5995](https://github.com/moment/moment/pull/5995) [bugfix] Remove const usage
+  * [#5990](https://github.com/moment/moment/pull/5990) misc: fix advisory link
+
+
+### 2.29.2 [See full changelog](https://gist.github.com/ichernev/1904b564f6679d9aac1ae08ce13bc45c)
+
+* Release Apr 3 2022
+
+Address https://github.com/moment/moment/security/advisories/GHSA-8hfj-j24r-96c4
+
+### 2.29.1 [See full changelog](https://gist.github.com/marwahaha/cc478ba01a1292ab4bd4e861d164d99b)
+
+* Release Oct 6, 2020
+
+Updated deprecation message, bugfix in hi locale
+
+### 2.29.0 [See full changelog](https://gist.github.com/marwahaha/b0111718641a6461800066549957ec14)
+
+* Release Sept 22, 2020
+
+New locales (es-mx, bn-bd).
+Minor bugfixes and locale improvements.
+More tests.
+Moment is in maintenance mode. Read more at this link:
+https://momentjs.com/docs/#/-project-status/
+
+### 2.28.0 [See full changelog](https://gist.github.com/marwahaha/028fd6c2b2470b2804857cfd63c0e94f)
+
+* Release Sept 13, 2020
+
+Fix bug where .format() modifies original instance, and locale updates
+
+### 2.27.0 [See full changelog](https://gist.github.com/marwahaha/5100c9c2f42019067b1f6cefc333daa7)
+
+* Release June 18, 2020
+
+Added Turkmen locale, other locale improvements, slight TypeScript fixes
+
+### 2.26.0 [See full changelog](https://gist.github.com/marwahaha/0725c40740560854a849b096ea7b7590)
+
+* Release May 19, 2020
+
+TypeScript fixes and many locale improvements
+
+### 2.25.3
+
+* Release May 4, 2020
+
+Remove package.json module property. It looks like webpack behaves differently
+for modules loaded via module vs jsnext:main.
+
+### 2.25.2
+
+* Release May 4, 2020
+
+This release includes ES Module bundled moment, separate from it's source code
+under dist/ folder. This might alleviate issues with finding the `./locale
+subfolder for loading locales. This might also mean now webpack will bundle all
+locales automatically, unless told otherwise.
+
+### 2.25.1
+
+* Release May 1, 2020
+
+This is a quick patch release to address some of the issues raised after
+releasing 2.25.0.
+
+* [2e268635](https://github.com/moment/moment/commit/2e268635) [misc] Revert #5269 due to webpack warning
+* [226799e1](https://github.com/moment/moment/commit/226799e1) [locale] fil: Fix metadata comment
+* [a83a521](https://github.com/moment/moment/commit/a83a521) [bugfix] Fix typeoff usages
+* [e324334](https://github.com/moment/moment/commit/e324334) [pkg] Add ts3.1-typings in npm package
+* [28cc23e](https://github.com/moment/moment/commit/28cc23e) [misc] Remove deleted generated locale en-SG
+
+### 2.25.0 [See full changelog](https://gist.github.com/ichernev/6148e64df2427e455b10ce6a18de1a65)
+
+* Release May 1, 2020
+
+* [#4611](https://github.com/moment/moment/issues/4611) [022dc038](https://github.com/moment/moment/commit/022dc038) [feature] Support for strict string parsing, fixes [#2469](https://github.com/moment/moment/issues/2469)
+* [#4599](https://github.com/moment/moment/issues/4599) [4b615b9d](https://github.com/moment/moment/commit/4b615b9d) [feature] Add support for eras in en and jp
+* [#4296](https://github.com/moment/moment/issues/4296) [757d4ff8](https://github.com/moment/moment/commit/757d4ff8) [feature] Accept custom relative thresholds in duration.humanize
+
+* 18 bigfixes
+* 36 locale fixes
+* 5 new locales (oc-lnc, zh-mo, en-in, gom-deva, fil)
+
+### 2.24.0 [See full changelog](https://gist.github.com/marwahaha/12366fe45bee328f33acf125d4cd540e)
+
+* Release Jan 21, 2019
+
+* [#4338](https://github.com/moment/moment/pull/4338) [bugfix] Fix startOf/endOf DST issues while boosting performance
+* [#4553](https://github.com/moment/moment/pull/4553) [feature] Add localeSort param to Locale weekday methods
+* [#4887](https://github.com/moment/moment/pull/4887) [bugfix] Make Duration#as work with quarters
+* 3 new locales (it-ch, ga, en-SG)
+* Lots of locale improvements
+
+### 2.23.0 [See full changelog](https://gist.github.com/marwahaha/eadb7ac11b761290399a576f8b2419a5)
+
+* Release Dec 12, 2018
+
+* [#4863](https://github.com/moment/moment/pull/4863) [new locale] added Kurdish language (ku)
+* [#4417](https://github.com/moment/moment/pull/4417) [bugfix] isBetween should return false for invalid dates
+* [#4700](https://github.com/moment/moment/pull/4700) [bugfix] Fix [#4698](https://github.com/moment/moment/pull/4698): Use ISO WeekYear for HTML5_FMT.WEEK
+* [#4563](https://github.com/moment/moment/pull/4563) [feature] Fix [#4518](https://github.com/moment/moment/pull/4518): Add support to add/subtract ISO weeks
+* other locale changes, build process changes, typos
+
+### 2.22.2 [See full changelog](https://gist.github.com/marwahaha/4d992c13c2dbc0f59d4d8acae1dc6d3a)
+
+* Release May 31, 2018
+
+* [#4564](https://github.com/moment/moment/pull/4564) [bugfix] Avoid using trim()
+* [#4453](https://github.com/moment/moment/pull/4453) [bugfix] Treat periods as periods, not regex-anything period, for weekday parsing in strict mode.
+* Minor locale improvements (pa-in, be, az)
+
+### 2.22.1 [See full changelog](https://gist.github.com/marwahaha/ff2cd13d0eda08afb7a237b10aae558c)
+
+* Release Apr 14, 2018
+
+* [#4495](https://github.com/moment/moment/pull/4495) [bugfix] Added HTML5_FMT to moment.d.ts
+* Minor locale improvements
+* QUnit upgrade and coveralls reporting
+
+### 2.22.0 [See full changelog](https://gist.github.com/marwahaha/ae895025dac3f0641fa9ec2e36d282bb)
+
+* Release Mar 30, 2018
+
+* [#4423](https://github.com/moment/moment/pull/4423) [new locale] Added Mongolian locale mn
+* Various locale improvements
+* Minor misc changes
+
+### 2.21.0 [See full changelog](https://gist.github.com/marwahaha/80d19ef882b71df1948df7865efdd40e)
+
+* Release Mar 2, 2018
+
+* [#4391](https://github.com/moment/moment/pull/4391) [bugfix] Fix [#4390](https://github.com/moment/moment/pull/4390): use offset properly in toISOString
+* [#4310](https://github.com/moment/moment/pull/4310) [bugfix] Fix [#3883](https://github.com/moment/moment/pull/3883) lazy load parentLocale in defineLocale, fallback to global if missing
+* [#4085](https://github.com/moment/moment/pull/4085) [misc] Print console warning when setting non-existent locales
+* [#4371](https://github.com/moment/moment/pull/4371) [misc] fix deprecated rollup options
+* New locales: ug-cn, en-il, tg
+* Various locale improvements
+
+### 2.20.1 [See changelog](https://gist.github.com/marwahaha/d72c1cb22076373be889b16272cbd187)
+
+* Release Dec 18, 2017
+
+* [#4359](https://github.com/moment/moment/pull/4359) [locale] Fix Arabic locale for months (again)
+* [#4357](https://github.com/moment/moment/pull/4357) [misc] Add optional parameter keepOffset to toISOString
+
+### 2.20.0 [See full changelog](https://gist.github.com/marwahaha/e0d4135fbf8bb75fa85c4aa2bddc5031)
+
+* Release Dec 16, 2017
+
+* [#4312](https://github.com/moment/moment/pull/4312) [bugfix] Fix [#4251](https://github.com/moment/moment/pull/4251): Avoid RFC2822 in utc() test
+* [#4240](https://github.com/moment/moment/pull/4240) [bugfix] Fix incorrect strict parsing with full-width parentheses
+* [#4341](https://github.com/moment/moment/pull/4341) [feature] Prevent toISOString converting to UTC (issue [#1751](https://github.com/moment/moment/pull/1751))
+* [#4154](https://github.com/moment/moment/pull/4154) [feature] add format constants to support output to HTML5 input type formats (see [#3928](https://github.com/moment/moment/pull/3928))
+* [#4143](https://github.com/moment/moment/pull/4143) [new locale] mt: Maltese language
+* [#4183](https://github.com/moment/moment/pull/4183) [locale] Relative seconds i18n
+* Various other locale improvements
+
+### 2.19.4 [See changelog](https://gist.github.com/marwahaha/d3b7b0ddf4bdae512244f16e8cc59efb)
+
+* Release Dec 10, 2017
+
+* [#4332](https://github.com/moment/moment/pull/4332) [bugfix] Fix weekday verification for UTC and offset days (fixes [#4227](https://github.com/moment/moment/pull/4227))
+* [#4336](https://github.com/moment/moment/pull/4336) [bugfix] Fix [#4334](https://github.com/moment/moment/pull/4334): Remove unused function call argument
+* [#4246](https://github.com/moment/moment/pull/4246) [misc] Add 'ss' relative time key to typescript definition
+
+### 2.19.3 [See changelog](https://gist.github.com/marwahaha/3654006bc0c2e522451c08d12c0bfabf)
+
+* Release Nov 29, 2017
+
+* [#4326](https://github.com/moment/moment/pull/4326) [bugfix] Fix for ReDOS vulnerability (see [#4163](https://github.com/moment/moment/issues/4163))
+* [#4289](https://github.com/moment/moment/pull/4289) [misc] Fix spelling and formatting for U.S. for es-us
+
+### 2.19.2 [See changelog (it's the same >:D)](https://gist.github.com/ichernev/76b1a3f33d3a8ff9665ce434a45221d0)
+
+* Release Nov 11, 2017
+
+* [#4255](https://github.com/moment/moment/pull/4255) [bugfix] Fix year setter for random days in a leap year, fixes [#4238](https://github.com/moment/moment/issues/4238)
+* [#4242](https://github.com/moment/moment/pull/4242) [bugfix] updateLocale now tries to load parent, fixes [#3626](https://github.com/moment/moment/issues/3626)
+
+### 2.19.1
+
+* Release Oct 11, 2017
+
+Make react native and webpack both work
+* #4225 #4226 #4232
+
+### 2.19.0 [See full changelog](https://gist.github.com/ichernev/5f3f4eb02761b4f765a0cccf02cec603)
+
+* Release Oct 10, 2017
+
+## Fix React Native 0.49+ crash
+* [#4213](https://github.com/moment/moment/pull/4213) [critical] Rename dynamic
+  require to avoid React Native crash
+* [#4214](https://github.com/moment/moment/pull/4214) [fixup] Move require
+  rename inside try/catch, fixes
+  [#4213](https://github.com/moment/moment/issues/4213)
+
+## Features
+
+* [#3735](https://github.com/moment/moment/pull/3735) [feature] Ignore NaN values in setters
+* [#4106](https://github.com/moment/moment/pull/4106) [fixup] Drop isNumeric utility fn, fixes [#3735](https://github.com/moment/moment/issues/3735)
+* [#4080](https://github.com/moment/moment/pull/4080) [feature] Implement a clone method for durations, fixes [#4078](https://github.com/moment/moment/issues/4078)
+* [#4215](https://github.com/moment/moment/pull/4215) [misc] TS: Add duration.clone(), for [#4080](https://github.com/moment/moment/issues/4080)
+
+## Packaging
+
+* [#4003](https://github.com/moment/moment/pull/4003) [pkg] bower: Remove tests from package
+* [#3904](https://github.com/moment/moment/pull/3904) [pkg] jsnext:main -> module in package.json
+* [#4060](https://github.com/moment/moment/pull/4060) [pkg] Account for new rollup interface
+
+Bugfixes, new locales, locale fixes etc...
+
+### 2.18.1
+
+* Release Mar 22, 2017
+
+* [#3853](https://github.com/moment/moment/pull/3853) [misc] Fix invalid whitespace character causing inability to parse
+  moment.js
+
+### 2.18.0 [See full changelog](https://gist.github.com/ichernev/78920c5a1e419fb28c6e4546d1b7235c)
+
+* Release Mar 18, 2017
+
+## Features
+
+* [#3708](https://github.com/moment/moment/pull/3708) [feature] RFC2822 parsing
+* [#3611](https://github.com/moment/moment/pull/3611) [feature] Durations gain validity
+* [#3738](https://github.com/moment/moment/pull/3738) [feature] Enable relative time for multiple seconds, request [#2558](https://github.com/moment/moment/issues/2558)
+* [#3766](https://github.com/moment/moment/pull/3766) [feature] Add support for k and kk format parsing
+
+## Bugfixes
+
+* [#3643](https://github.com/moment/moment/pull/3643) [bugfix] Fixes [#3520](https://github.com/moment/moment/issues/3520), parseZone incorrectly handled minutes under 16
+* [#3710](https://github.com/moment/moment/pull/3710) [bugfix] Fixes [#3632](https://github.com/moment/moment/issues/3632), toISOString returns null for invalid date
+* [#3787](https://github.com/moment/moment/pull/3787) [bugfix] Fixes [#3717](https://github.com/moment/moment/issues/3717), ensure day-of-year is non-zero
+* [#3780](https://github.com/moment/moment/pull/3780) [bugfix] Fixes [#3765](https://github.com/moment/moment/issues/3765): Ensure year 0 is formatted with YYYY
+* [#3806](https://github.com/moment/moment/pull/3806) [bugfix] Fixes [#3805](https://github.com/moment/moment/issues/3805), fix locale month getters for standalone/format cases
+
+7 new locales, many locale improvements and some misc changes
+
+### 2.17.1 [Also available here](https://gist.github.com/ichernev/f38280b2b29c4932914a6d3a4e50bfb2)
+* Release Dec 03, 2016
+
+* [#3638](https://github.com/moment/moment/pull/3638) [misc] TS: Make typescript definitions work with 1.x
+* [#3628](https://github.com/moment/moment/pull/3628) [misc] Adds "sign CLA" link to `CONTRIBUTING.md`
+* [#3640](https://github.com/moment/moment/pull/3640) [misc] Fix locale issues
+
+### 2.17.0 [Also available here](https://gist.github.com/ichernev/ed58f76fb95205eeac653d719972b90c)
+* Release Nov 22, 2016
+
+* [#3435](https://github.com/moment/moment/pull/3435) [new locale] yo: Yoruba (Nigeria) locale
+* [#3595](https://github.com/moment/moment/pull/3595) [bugfix] Fix accidental reference to global "value" variable
+* [#3506](https://github.com/moment/moment/pull/3506) [bugfix] Fix invalid moments returning valid dates to method calls
+* [#3563](https://github.com/moment/moment/pull/3563) [locale] ca: Change future relative time
+* [#3504](https://github.com/moment/moment/pull/3504) [tests] Fixes [#3463](https://github.com/moment/moment/issues/3463), parseZone not handling Z correctly (tests only)
+* [#3591](https://github.com/moment/moment/pull/3591) [misc] typescript: update typescript to 2.0.8, add strictNullChecks=true
+* [#3597](https://github.com/moment/moment/pull/3597) [misc] Fixed capitalization in nuget spec
+
+### 2.16.0 [See full changelog](https://gist.github.com/ichernev/17bffc1005a032cb1a8ac4c1558b4994)
+* Release Nov 9, 2016
+
+## Features
+* [#3530](https://github.com/moment/moment/pull/3530) [feature] Check whether input is date before checking if format is array
+* [#3515](https://github.com/moment/moment/pull/3515) [feature] Fix [#2300](https://github.com/moment/moment/issues/2300): Default to current week.
+
+## Bugfixes
+* [#3546](https://github.com/moment/moment/pull/3546) [bugfix] Implement lazy-loading of child locales with missing prents
+* [#3523](https://github.com/moment/moment/pull/3523) [bugfix] parseZone should handle UTC
+* [#3502](https://github.com/moment/moment/pull/3502) [bugfix] Fix [#3500](https://github.com/moment/moment/issues/3500): ISO 8601 parsing should match the full string, not the beginning of the string.
+* [#3581](https://github.com/moment/moment/pull/3581) [bugfix] Fix parseZone, redo [#3504](https://github.com/moment/moment/issues/3504), fix [#3463](https://github.com/moment/moment/issues/3463)
+
+## New Locales
+* [#3416](https://github.com/moment/moment/pull/3416) [new locale] nl-be: Dutch (Belgium) locale
+* [#3393](https://github.com/moment/moment/pull/3393) [new locale] ar-dz: Arabic (Algeria) locale
+* [#3342](https://github.com/moment/moment/pull/3342) [new locale] tet: Tetun Dili (East Timor) locale
+
+And more locale, build and typescript improvements
+
+### 2.15.2
+* Release Oct 23, 2016
+* [#3525](https://github.com/moment/moment/pull/3525) Speedup month standalone/format regexes **(IMPORTANT)**
+* [#3466](https://github.com/moment/moment/pull/3466) Fix typo of Javanese
+
+### 2.15.1
+* Release Sept 20, 2016
+* [#3438](https://github.com/moment/moment/pull/3438) Fix locale autoload, revert [#3344](https://github.com/moment/moment/pull/3344)
+
+### 2.15.0 [See full changelog](https://gist.github.com/ichernev/10e1c5bf647545c72ca30e9628a09ed3)
+- Release Sept 12, 2016
+
+## New Locales
+* [#3255](https://github.com/moment/moment/pull/3255) [new locale] mi: Maori language
+* [#3267](https://github.com/moment/moment/pull/3267) [new locale] ar-ly: Arabic (Libya) locale
+* [#3333](https://github.com/moment/moment/pull/3333) [new locale] zh-hk: Chinese (Hong Kong) locale
+
+## Bugfixes
+* [#3276](https://github.com/moment/moment/pull/3276) [bugfix] duration: parser: Support ms durations in .NET syntax
+* [#3312](https://github.com/moment/moment/pull/3312) [bugfix] locales: Enable locale-data getters without moment (fixes [#3284](https://github.com/moment/moment/issues/3284))
+* [#3381](https://github.com/moment/moment/pull/3381) [bugfix] parsing: Fix parseZone without timezone in string, fixes [#3083](https://github.com/moment/moment/issues/3083)
+* [#3383](https://github.com/moment/moment/pull/3383) [bugfix] toJSON: Fix isValid so that toJSON works after a moment is frozen
+* [#3427](https://github.com/moment/moment/pull/3427) [bugfix] ie8: Fix IE8 (regression in 2.14.x)
+
+## Packaging
+* [#3299](https://github.com/moment/moment/pull/3299) [pkg] npm: Do not include .npmignore in npm package
+* [#3273](https://github.com/moment/moment/pull/3273) [pkg] jspm: Include moment.d.ts file in package
+* [#3344](https://github.com/moment/moment/pull/3344) [pkg] exports: use module.require for nodejs
+
+Also some locale and typescript improvements
+
+### 2.14.1
+- Release July 20, 2016
+* [#3280](https://github.com/moment/moment/pull/3280) Fix typescript definitions
+
+
+### 2.14.0 [See full changelog](https://gist.github.com/ichernev/812e79ac36a7829a22598fe964bfc18a)
+
+- Release July 20, 2016
+
+## New Features
+* [#3233](https://github.com/moment/moment/pull/3233) Introduce month.isFormat for format/standalone discovery
+* [#2848](https://github.com/moment/moment/pull/2848) Allow user to get/set the rounding method used when calculating relative time
+* [#3112](https://github.com/moment/moment/pull/3112) optimize configFromStringAndFormat
+* [#3147](https://github.com/moment/moment/pull/3147) Call calendar format function with moment context
+* [#3160](https://github.com/moment/moment/pull/3160) deprecate isDSTShifted
+* [#3175](https://github.com/moment/moment/pull/3175) make moment calendar extensible with ad-hoc options
+* [#3191](https://github.com/moment/moment/pull/3191) toDate returns a copy of the internal date object
+* [#3192](https://github.com/moment/moment/pull/3192) Adding support for rollup import.
+* [#3238](https://github.com/moment/moment/pull/3238) Handle empty object and empty array for creation as now
+* [#3082](https://github.com/moment/moment/pull/3082) Use relative AMD moment dependency
+
+## Bugfixes
+* [#3241](https://github.com/moment/moment/pull/3241) Escape all 24 mixed pieces, not only first 12 in computeMonthsParse
+* [#3008](https://github.com/moment/moment/pull/3008) Object setter orders sets based on size of unit
+* [#3177](https://github.com/moment/moment/pull/3177) Bug Fix [#2704](https://github.com/moment/moment/pull/2704) - isoWeekday(String) inconsistent with isoWeekday(Number)
+* [#3230](https://github.com/moment/moment/pull/3230) fix passing date with format string to ignore format string
+* [#3232](https://github.com/moment/moment/pull/3232) Fix negative 0 in certain diff cases
+* [#3235](https://github.com/moment/moment/pull/3235) Use proper locale inheritance for the base locale, fixes [#3137](https://github.com/moment/moment/pull/3137)
+
+Plus es-do locale and locale bugfixes
+
+### 2.13.0 [See full changelog](https://gist.github.com/ichernev/0132fcf5b61f7fc140b0bb0090480d49)
+- Release April 18, 2016
+
+## Enhancements:
+* [#2982](https://github.com/moment/moment/pull/2982) Add 'date' as alias to 'day' for startOf() and endOf().
+* [#2955](https://github.com/moment/moment/pull/2955) Add parsing negative components in durations when ISO 8601
+* [#2991](https://github.com/moment/moment/pull/2991) isBetween support for both open and closed intervals
+* [#3105](https://github.com/moment/moment/pull/3105) Add localeSorted argument to weekday listers
+* [#3102](https://github.com/moment/moment/pull/3102) Add k and kk formatting tokens
+
+## Bugfixes
+* [#3109](https://github.com/moment/moment/pull/3109) Fix [#1756](https://github.com/moment/moment/issues/1756) Resolved thread-safe issue on server side.
+* [#3078](https://github.com/moment/moment/pull/3078) Fix parsing for months/weekdays with weird characters
+* [#3098](https://github.com/moment/moment/pull/3098) Use Z suffix when in UTC mode ([#3020](https://github.com/moment/moment/issues/3020))
+* [#2995](https://github.com/moment/moment/pull/2995) Fix floating point rounding errors in durations
+* [#3059](https://github.com/moment/moment/pull/3059) fix bug where diff returns -0 in month-related diffs
+* [#3045](https://github.com/moment/moment/pull/3045) Fix mistaking any input for 'a' token
+* [#2877](https://github.com/moment/moment/pull/2877) Use explicit .valueOf() calls instead of coercion
+* [#3036](https://github.com/moment/moment/pull/3036) Year setter should keep time when DST changes
+
+Plus 3 new locales and locale fixes.
+
+### 2.12.0 [See full changelog](https://gist.github.com/ichernev/6e5bfdf8d6522fc4ac73)
+
+- Release March 7, 2016
+
+## Enhancements:
+* [#2932](https://github.com/moment/moment/pull/2932) List loaded locales
+* [#2818](https://github.com/moment/moment/pull/2818) Parse ISO-8061 duration containing both day and week values
+* [#2774](https://github.com/moment/moment/pull/2774) Implement locale inheritance and locale updating
+
+## Bugfixes:
+* [#2970](https://github.com/moment/moment/pull/2970) change add subtract to handle decimal values by rounding
+* [#2887](https://github.com/moment/moment/pull/2887) Fix toJSON casting of invalid moment
+* [#2897](https://github.com/moment/moment/pull/2897) parse string arguments for month() correctly, closes #2884
+* [#2946](https://github.com/moment/moment/pull/2946) Fix usage suggestions for min and max
+
+## New locales:
+* [#2917](https://github.com/moment/moment/pull/2917) Locale Punjabi(Gurmukhi) India format conversion
+
+And more
+
+### 2.11.2 (Fix ReDoS attack vector)
+
+- Release February 7, 2016
+
+* [#2939](https://github.com/moment/moment/pull/2939) use full-string match to speed up aspnet regex match
+
+### 2.11.1 [See full changelog](https://gist.github.com/ichernev/8ec3ee25b749b4cff3c2)
+
+- Release January 9, 2016
+
+## Bugfixes:
+* [#2881](https://github.com/moment/moment/pull/2881) Revert "Merge pull request #2746 from mbad0la:develop" Sep->Sept
+* [#2868](https://github.com/moment/moment/pull/2868) Add format and parse token Y, so it actually works
+* [#2865](https://github.com/moment/moment/pull/2865) Use typeof checks for undefined for global variables
+* [#2858](https://github.com/moment/moment/pull/2858) Fix Date mocking regression introduced in 2.11.0
+* [#2864](https://github.com/moment/moment/pull/2864) Include changelog in npm release
+* [#2830](https://github.com/moment/moment/pull/2830) dep: add grunt-cli
+* [#2869](https://github.com/moment/moment/pull/2869) Fix months parsing for some locales
+
+### 2.11.0 [See full changelog](https://gist.github.com/ichernev/6594bc29719dde6b2f66)
+
+- Release January 4, 2016
+
+* [#2624](https://github.com/moment/moment/pull/2624) Proper handling of invalid moments
+* [#2634](https://github.com/moment/moment/pull/2634) Fix strict month parsing issue in cs,ru,sk
+* [#2735](https://github.com/moment/moment/pull/2735) Reset the locale back to 'en' after defining all locales in min/locales.js
+* [#2702](https://github.com/moment/moment/pull/2702) Week rework
+* [#2746](https://github.com/moment/moment/pull/2746) Changed September Abbreviation to "Sept" in locale-specific english
+  files and default locale file
+* [#2646](https://github.com/moment/moment/pull/2646) Fix [#2645](https://github.com/moment/moment/pull/2645) - invalid dates pre-1970
+
+* [#2641](https://github.com/moment/moment/pull/2641) Implement basic format and comma as ms separator in ISO 8601
+* [#2665](https://github.com/moment/moment/pull/2665) Implement stricter weekday parsing
+* [#2700](https://github.com/moment/moment/pull/2700) Add [Hh]mm and [Hh]mmss formatting tokens, so you can parse 123 with
+  hmm for example
+* [#2565](https://github.com/moment/moment/pull/2565) [#2835](https://github.com/moment/moment/pull/2835) Expose arguments used for moment creation with creationData
+  (fix [#2443](https://github.com/moment/moment/pull/2443))
+* [#2648](https://github.com/moment/moment/pull/2648) fix issue [#2640](https://github.com/moment/moment/pull/2640): support instanceof operator
+* [#2709](https://github.com/moment/moment/pull/2709) Add isSameOrAfter and isSameOrBefore comparison methods
+* [#2721](https://github.com/moment/moment/pull/2721) Fix moment creation from object with strings values
+* [#2740](https://github.com/moment/moment/pull/2740) Enable 'd hh:mm:ss.sss' format for durations
+* [#2766](https://github.com/moment/moment/pull/2766) [#2833](https://github.com/moment/moment/pull/2833) Alternate Clock Source Support
+
+### 2.10.6
+
+- Release July 28, 2015
+
+[#2515](https://github.com/moment/moment/pull/2515) Fix regression introduced
+in `2.10.5` related to `moment.ISO_8601` parsing.
+
+### 2.10.5 [See full changelog](https://gist.github.com/ichernev/6ec13ac7efc396da44b2)
+
+- Release July 26, 2015
+
+Important changes:
+* [#2357](https://github.com/moment/moment/pull/2357) Improve unit bubbling for ISO dates
+  this fixes day to year conversions to work around end-of-year (~365 days). As
+  a side effect 365 days is 11 months and 30 days, and 366 days is one year.
+* [#2438](https://github.com/moment/moment/pull/2438) Fix inconsistent moment.min and moment.max results
+  Return invalid result if any of the inputs is invalid
+* [#2494](https://github.com/moment/moment/pull/2494) Fix two digit year parsing with YYYY format
+  This brings the benefits of YY to YYYY
+* [#2368](https://github.com/moment/moment/pull/2368) perf: use faster form of copying dates, across the board improvement
+
+
+### 2.10.3 [See full changelog](https://gist.github.com/ichernev/f264b9bed5b00f8b1b7f)
+
+- Release May 13, 2015
+
+* add `moment.fn.to` and `moment.fn.toNow` (similar to `from` and `fromNow`)
+* new locales (Sinhalese (si), Montenegrin (me), Javanese (ja))
+* performance improvements
+
+### 2.10.2
+
+- Release April 9, 2015
+
+* fixed moment-with-locales in browser env caused by esperanto change
+
+### 2.10.1
+
+* regression: Add moment.duration.fn back
+
+### 2.10.0
+
+Ported code to es6 modules.
+
+### 2.9.0 [See full changelog](https://gist.github.com/ichernev/0c9a9b49951111a27ce7)
+
+- Release January 8, 2015
+
+languages:
+* [2104](https://github.com/moment/moment/issues/2104) Frisian (fy) language file with unit test
+* [2097](https://github.com/moment/moment/issues/2097) add ar-tn locale
+
+deprecations:
+* [2074](https://github.com/moment/moment/issues/2074) Implement `moment.fn.utcOffset`, deprecate `moment.fn.zone`
+
+features:
+* [2088](https://github.com/moment/moment/issues/2088) add moment.fn.isBetween
+* [2054](https://github.com/moment/moment/issues/2054) Call updateOffset when creating moment (needed for default timezone in
+  moment-timezone)
+* [1893](https://github.com/moment/moment/issues/1893) Add moment.isDate method
+* [1825](https://github.com/moment/moment/issues/1825) Implement toJSON function on Duration
+* [1809](https://github.com/moment/moment/issues/1809) Allowing moment.set() to accept a hash of units
+* [2128](https://github.com/moment/moment/issues/2128) Add firstDayOfWeek, firstDayOfYear locale getters
+* [2131](https://github.com/moment/moment/issues/2131) Add quarter diff support
+
+Some bugfixes and language improvements -- [full changelog](https://gist.github.com/ichernev/0c9a9b49951111a27ce7)
+
+### 2.8.4 [See full changelog](https://gist.github.com/ichernev/a4fcb0a46d74e4b9b996)
+
+- Release November 19, 2014
+
+Features:
+
+* [#2000](https://github.com/moment/moment/issues/2000) Add LTS localised format that includes seconds
+* [#1960](https://github.com/moment/moment/issues/1960) added formatToken 'x' for unix offset in milliseconds #1938
+* [#1965](https://github.com/moment/moment/issues/1965) Support 24:00:00.000 to mean next day, at midnight.
+* [#2002](https://github.com/moment/moment/issues/2002) Accept 'date' key when creating moment with object
+* [#2009](https://github.com/moment/moment/issues/2009) Use native toISOString when we can
+
+Some bugfixes and language improvements -- [full changelog](https://gist.github.com/ichernev/a4fcb0a46d74e4b9b996)
+
+### 2.8.3
+
+- Release September 5, 2014
+
+Bugfixes:
+
+* [#1801](https://github.com/moment/moment/issues/1801) proper pluralization for Arabic
+* [#1833](https://github.com/moment/moment/issues/1833) improve spm integration
+* [#1871](https://github.com/moment/moment/issues/1871) fix zone bug caused by Firefox 24
+* [#1882](https://github.com/moment/moment/issues/1882) Use hh:mm in Czech
+* [#1883](https://github.com/moment/moment/issues/1883) Fix 2.8.0 regression in duration as conversions
+* [#1890](https://github.com/moment/moment/issues/1890) Faster travis builds
+* [#1892](https://github.com/moment/moment/issues/1892) Faster isBefore/After/Same
+* [#1848](https://github.com/moment/moment/issues/1848) Fix flaky month diffs
+* [#1895](https://github.com/moment/moment/issues/1895) Fix 2.8.0 regression in moment.utc with format array
+* [#1896](https://github.com/moment/moment/issues/1896) Support setting invalid instance locale (noop)
+* [#1897](https://github.com/moment/moment/issues/1897) Support moment([str]) in addition to moment([int])
+
+### 2.8.2
+
+- Release August 22, 2014
+
+Minor bugfixes:
+
+* [#1874](https://github.com/moment/moment/issues/1874) use `Object.prototype.hasOwnProperty`
+  instead of `obj.hasOwnProperty` (ie8 bug)
+* [#1873](https://github.com/moment/moment/issues/1873) add `duration#toString()`
+* [#1859](https://github.com/moment/moment/issues/1859) better month/weekday names in norwegian
+* [#1812](https://github.com/moment/moment/issues/1812) meridiem parsing for greek
+* [#1804](https://github.com/moment/moment/issues/1804) spanish del -> de
+* [#1800](https://github.com/moment/moment/issues/1800) korean LT improvement
+
+### 2.8.1
+
+- Release August 1, 2014
+
+* bugfix [#1813](https://github.com/moment/moment/issues/1813): fix moment().lang([key]) incompatibility
+
+### 2.8.0 [See changelog](https://gist.github.com/ichernev/ac3899324a5fa6c8c9b4)
+
+- Release July 31, 2014
+
+* incompatible changes
+    * [#1761](https://github.com/moment/moment/issues/1761): moments created without a language are no longer following the global language, in case it changes. Only newly created moments take the global language by default. In case you're affected by this, wait, comment on [#1797](https://github.com/moment/moment/issues/1797) and wait for a proper reimplementation
+    * [#1642](https://github.com/moment/moment/issues/1642): 45 days is no longer "a month" according to humanize, cutoffs for month, and year have changed. Hopefully your code does not depend on a particular answer from humanize (which it shouldn't anyway)
+    * [#1784](https://github.com/moment/moment/issues/1784): if you use the human readable English datetime format in a weird way (like storing them in a database) that would break when the format changes you're at risk.
+
+* deprecations (old behavior will be dropped in 3.0)
+    * [#1761](https://github.com/moment/moment/issues/1761) `lang` is renamed to `locale`, `langData` -> `localeData`. Also there is now `defineLocale` that should be used when creating new locales
+    * [#1763](https://github.com/moment/moment/issues/1763) `add(unit, value)` and `subtract(unit, value)` are now deprecated. Use `add(value, unit)` and `subtract(value, unit)` instead.
+    * [#1759](https://github.com/moment/moment/issues/1759) rename `duration.toIsoString` to `duration.toISOString`. The js standard library and moment's `toISOString` follow that convention.
+
+* new locales
+    * [#1789](https://github.com/moment/moment/issues/1789) Tibetan (bo)
+    * [#1786](https://github.com/moment/moment/issues/1786) Africaans (af)
+    * [#1778](https://github.com/moment/moment/issues/1778) Burmese (my)
+    * [#1727](https://github.com/moment/moment/issues/1727) Belarusian (be)
+
+* bugfixes, locale bugfixes, performance improvements, features
+
+### 2.7.0 [See changelog](https://gist.github.com/ichernev/b0a3d456d5a84c9901d7)
+
+- Release June 12, 2014
+
+* new languages
+
+  * [#1678](https://github.com/moment/moment/issues/1678) Bengali (bn)
+  * [#1628](https://github.com/moment/moment/issues/1628) Azerbaijani (az)
+  * [#1633](https://github.com/moment/moment/issues/1633) Arabic, Saudi Arabia (ar-sa)
+  * [#1648](https://github.com/moment/moment/issues/1648) Austrian German (de-at)
+
+* features
+
+  * [#1663](https://github.com/moment/moment/issues/1663) configurable relative time thresholds
+  * [#1554](https://github.com/moment/moment/issues/1554) support anchor time in moment.calendar
+  * [#1693](https://github.com/moment/moment/issues/1693) support moment.ISO_8601 as parsing format
+  * [#1637](https://github.com/moment/moment/issues/1637) add moment.min and moment.max and deprecate min/max instance methods
+  * [#1704](https://github.com/moment/moment/issues/1704) support string value in add/subtract
+  * [#1647](https://github.com/moment/moment/issues/1647) add spm support (package manager)
+
+* bugfixes
+
+### 2.6.0 [See changelog](https://gist.github.com/ichernev/10544682)
+
+- Release April 12 , 2014
+
+* languages
+  * [#1529](https://github.com/moment/moment/issues/1529) Serbian-Cyrillic (sr-cyr)
+  * [#1544](https://github.com/moment/moment/issues/1544), [#1546](https://github.com/moment/moment/issues/1546) Khmer Cambodia (km)
+
+* features
+    * [#1419](https://github.com/moment/moment/issues/1419), [#1468](https://github.com/moment/moment/issues/1468), [#1467](https://github.com/moment/moment/issues/1467), [#1546](https://github.com/moment/moment/issues/1546) better handling of timezone-d moments around DST
+    * [#1462](https://github.com/moment/moment/issues/1462) add weeksInYear and isoWeeksInYear
+    * [#1475](https://github.com/moment/moment/issues/1475) support ordinal parsing
+    * [#1499](https://github.com/moment/moment/issues/1499) composer support
+    * [#1577](https://github.com/moment/moment/issues/1577), [#1604](https://github.com/moment/moment/issues/1604) put Date parsing in moment.createFromInputFallback so it can be properly deprecated and controlled in the future
+    * [#1545](https://github.com/moment/moment/issues/1545) extract two-digit year parsing in moment.parseTwoDigitYear, so it can be overwritten
+    * [#1590](https://github.com/moment/moment/issues/1590) (see [#1574](https://github.com/moment/moment/issues/1574)) set AMD global before module definition to better support non AMD module dependencies used in AMD environment
+    * [#1589](https://github.com/moment/moment/issues/1589) remove global in Node.JS environment (was not working before, nobody complained, was scheduled for removal anyway)
+    * [#1586](https://github.com/moment/moment/issues/1586) support quarter setting and parsing
+
+* 18 bugs fixed
+
+### 2.5.1
+
+- Release January 22, 2014
+
+* languages
+  * [#1392](https://github.com/moment/moment/issues/1392) Armenian (hy-am)
+
+* bugfixes
+  * [#1429](https://github.com/moment/moment/issues/1429) fixes [#1423](https://github.com/moment/moment/issues/1423) weird chrome-32 bug with js object creation
+  * [#1421](https://github.com/moment/moment/issues/1421) remove html entities from Welsh
+  * [#1418](https://github.com/moment/moment/issues/1418) fixes [#1401](https://github.com/moment/moment/issues/1401) improved non-padded tokens in strict matching
+  * [#1417](https://github.com/moment/moment/issues/1417) fixes [#1404](https://github.com/moment/moment/issues/1404) handle buggy moment object created by property cloning
+  * [#1398](https://github.com/moment/moment/issues/1398) fixes [#1397](https://github.com/moment/moment/issues/1397) fix Arabic-like week number parsing
+  * [#1396](https://github.com/moment/moment/issues/1396) add leftZeroFill(4) to GGGG and gggg formats
+  * [#1373](https://github.com/moment/moment/issues/1373) use lowercase for months and days in Catalan
+
+* testing
+  * [#1374](https://github.com/moment/moment/issues/1374) run tests on multiple browser/os combos via SauceLabs and Travis
+
+### 2.5.0 [See changelog](https://gist.github.com/ichernev/8104451)
+
+- Release Dec 24, 2013
+
+* New languages
+  * Luxemburish (lb) [1247](https://github.com/moment/moment/issues/1247)
+  * Serbian (rs) [1319](https://github.com/moment/moment/issues/1319)
+  * Tamil (ta) [1324](https://github.com/moment/moment/issues/1324)
+  * Macedonian (mk) [1337](https://github.com/moment/moment/issues/1337)
+
+* Features
+  * [1311](https://github.com/moment/moment/issues/1311) Add quarter getter and format token `Q`
+  * [1303](https://github.com/moment/moment/issues/1303) strict parsing now respects number of digits per token (fix [1196](https://github.com/moment/moment/issues/1196))
+  * 0d30bb7 add jspm support
+  * [1347](https://github.com/moment/moment/issues/1347) improve zone parsing
+  * [1362](https://github.com/moment/moment/issues/1362) support merideam parsing in Korean
+
+* 22 bugfixes
+
+### 2.4.0
+
+- Release Oct 27, 2013
+
+* **Deprecate** globally exported moment, will be removed in next major
+* New languages
+  * Farose (fo) [#1206](https://github.com/moment/moment/issues/1206)
+  * Tagalog/Filipino (tl-ph) [#1197](https://github.com/moment/moment/issues/1197)
+  * Welsh (cy) [#1215](https://github.com/moment/moment/issues/1215)
+* Bugfixes
+  * properly handle Z at the end of iso RegExp [#1187](https://github.com/moment/moment/issues/1187)
+  * chinese meridian time improvements [#1076](https://github.com/moment/moment/issues/1076)
+  * fix language tests [#1177](https://github.com/moment/moment/issues/1177)
+  * remove some failing tests (that should have never existed :))
+    [#1185](https://github.com/moment/moment/issues/1185)
+    [#1183](https://github.com/moment/moment/issues/1183)
+  * handle russian noun cases in weird cases [#1195](https://github.com/moment/moment/issues/1195)
+
+### 2.3.1
+
+- Release Oct 9, 2013
+
+Removed a trailing comma [1169] and fixed a bug with `months`, `weekdays` getters [#1171](https://github.com/moment/moment/issues/1171).
+
+### 2.3.0 [See changelog](https://gist.github.com/ichernev/6864354)
+
+- Release Oct 7, 2013
+
+Changed isValid, added strict parsing.
+Week tokens parsing.
+
+### 2.2.1
+
+- Release Sep 12, 2013
+
+Fixed bug in string prototype test.
+Updated authors and contributors.
+
+### 2.2.0 [See changelog](https://gist.github.com/ichernev/00f837a9baf46a3565e4)
+
+- Release  Sep 11, 2013
+
+Added bower support.
+
+Language files now use UMD.
+
+Creating moment defaults to current date/month/year.
+
+Added a bundle of moment and all language files.
+
+### 2.1.0 [See changelog](https://gist.github.com/timrwood/b8c2d90d528eddb53ab5)
+
+- Release Jul 8, 2013
+
+Added better week support.
+
+Added ability to set offset with `moment#zone`.
+
+Added ability to set month or weekday from a string.
+
+Added `moment#min` and `moment#max`
+
+### 2.0.0 [See changelog](https://gist.github.com/timrwood/e72f2eef320ed9e37c51)
+
+- Release Feb 9, 2013
+
+Added short form localized tokens.
+
+Added ability to define language a string should be parsed in.
+
+Added support for reversed add/subtract arguments.
+
+Added support for `endOf('week')` and `startOf('week')`.
+
+Fixed the logic for `moment#diff(Moment, 'months')` and `moment#diff(Moment, 'years')`
+
+`moment#diff` now floors instead of rounds.
+
+Normalized `moment#toString`.
+
+Added `isSame`, `isAfter`, and `isBefore` methods.
+
+Added better week support.
+
+Added `moment#toJSON`
+
+Bugfix: Fixed parsing of first century dates
+
+Bugfix: Parsing 10Sep2001 should work as expected
+
+Bugfix: Fixed weirdness with `moment.utc()` parsing.
+
+Changed language ordinal method to return the number + ordinal instead of just the ordinal.
+
+Changed two digit year parsing cutoff to match strptime.
+
+Removed `moment#sod` and `moment#eod` in favor of `moment#startOf` and `moment#endOf`.
+
+Removed `moment.humanizeDuration()` in favor of `moment.duration().humanize()`.
+
+Removed the lang data objects from the top level namespace.
+
+Duplicate `Date` passed to `moment()` instead of referencing it.
+
+### 1.7.2 [See discussion](https://github.com/timrwood/moment/issues/456)
+
+- Release Oct 2, 2012
+
+Bugfixes
+
+### 1.7.1 [See discussion](https://github.com/timrwood/moment/issues/384)
+
+- Release Oct 1, 2012
+
+Bugfixes
+
+### 1.7.0 [See discussion](https://github.com/timrwood/moment/issues/288)
+
+- Release Jul 26, 2012
+
+Added `moment.fn.endOf()` and `moment.fn.startOf()`.
+
+Added validation via `moment.fn.isValid()`.
+
+Made formatting method 3x faster. http://jsperf.com/momentjs-cached-format-functions
+
+Add support for month/weekday callbacks in `moment.fn.format()`
+
+Added instance specific languages.
+
+Added two letter weekday abbreviations with the formatting token `dd`.
+
+Various language updates.
+
+Various bugfixes.
+
+### 1.6.0 [See discussion](https://github.com/timrwood/moment/pull/268)
+
+- Release Apr 26, 2012
+
+Added Durations.
+
+Revamped parser to support parsing non-separated strings (YYYYMMDD vs YYYY-MM-DD).
+
+Added support for millisecond parsing and formatting tokens (S SS SSS)
+
+Added a getter for `moment.lang()`
+
+Various bugfixes.
+
+There are a few things deprecated in the 1.6.0 release.
+
+1. The format tokens `z` and `zz` (timezone abbreviations like EST CST MST etc) will no longer be supported. Due to inconsistent browser support, we are unable to consistently produce this value. See [this issue](https://github.com/timrwood/moment/issues/162) for more background.
+
+2. The method `moment.fn.native` is deprecated in favor of `moment.fn.toDate`. There continue to be issues with Google Closure Compiler throwing errors when using `native`, even in valid instances.
+
+3. The way to customize am/pm strings is being changed. This would only affect you if you created a custom language file. For more information, see [this issue](https://github.com/timrwood/moment/pull/222).
+
+### 1.5.0 [See milestone](https://github.com/timrwood/moment/issues?milestone=10&page=1&state=closed)
+
+- Release Mar 20, 2012
+
+Added UTC mode.
+
+Added automatic ISO8601 parsing.
+
+Various bugfixes.
+
+### 1.4.0 [See milestone](https://github.com/timrwood/moment/issues?milestone=8&state=closed)
+
+- Release Feb 4, 2012
+
+Added `moment.fn.toDate` as a replacement for `moment.fn.native`.
+
+Added `moment.fn.sod` and `moment.fn.eod` to get the start and end of day.
+
+Various bugfixes.
+
+### 1.3.0 [See milestone](https://github.com/timrwood/moment/issues?milestone=7&state=closed)
+
+- Release Jan 5, 2012
+
+Added support for parsing month names in the current language.
+
+Added escape blocks for parsing tokens.
+
+Added `moment.fn.calendar` to format strings like 'Today 2:30 PM', 'Tomorrow 1:25 AM', and 'Last Sunday 4:30 AM'.
+
+Added `moment.fn.day` as a setter.
+
+Various bugfixes
+
+### 1.2.0 [See milestone](https://github.com/timrwood/moment/issues?milestone=4&state=closed)
+
+- Release Dec 7, 2011
+
+Added timezones to parser and formatter.
+
+Added `moment.fn.isDST`.
+
+Added `moment.fn.zone` to get the timezone offset in minutes.
+
+### 1.1.2 [See milestone](https://github.com/timrwood/moment/issues?milestone=6&state=closed)
+
+- Release Nov 18, 2011
+
+Various bugfixes
+
+### 1.1.1 [See milestone](https://github.com/timrwood/moment/issues?milestone=5&state=closed)
+
+- Release Nov 12, 2011
+
+Added time specific diffs (months, days, hours, etc)
+
+### 1.1.0
+
+- Release Oct 28, 2011
+
+Added `moment.fn.format` localized masks. 'L LL LLL LLLL' [issue 29](https://github.com/timrwood/moment/pull/29)
+
+Fixed [issue 31](https://github.com/timrwood/moment/pull/31).
+
+### 1.0.1
+
+- Release Oct 18, 2011
+
+Added `moment.version` to get the current version.
+
+Removed `window !== undefined` when checking if module exists to support browserify. [issue 25](https://github.com/timrwood/moment/pull/25)
+
+### 1.0.0
+
+- Release
+
+Added convenience methods for getting and setting date parts.
+
+Added better support for `moment.add()`.
+
+Added better lang support in NodeJS.
+
+Renamed library from underscore.date to Moment.js
+
+### 0.6.1
+
+- Release Oct 12, 2011
+
+Added Portuguese, Italian, and French language support
+
+### 0.6.0
+
+- Release Sep 21, 2011
+
+Added _date.lang() support.
+Added support for passing multiple formats to try to parse a date. _date("07-10-1986", ["MM-DD-YYYY", "YYYY-MM-DD"]);
+Made parse from string and single format 25% faster.
+
+### 0.5.2
+
+- Release Jul 11, 2011
+
+Bugfix for [issue 8](https://github.com/timrwood/underscore.date/pull/8) and [issue 9](https://github.com/timrwood/underscore.date/pull/9).
+
+### 0.5.1
+
+- Release Jun 17, 2011
+
+Bugfix for [issue 5](https://github.com/timrwood/underscore.date/pull/5).
+
+### 0.5.0
+
+- Release Jun 13, 2011
+
+Dropped the redundant `_date.date()` in favor of `_date()`.
+Removed `_date.now()`, as it is a duplicate of `_date()` with no parameters.
+Removed `_date.isLeapYear(yearNumber)`. Use `_date([yearNumber]).isLeapYear()` instead.
+Exposed customization options through the `_date.relativeTime`, `_date.weekdays`, `_date.weekdaysShort`, `_date.months`, `_date.monthsShort`, and `_date.ordinal` variables instead of the `_date.customize()` function.
+
+### 0.4.1
+
+- Release May 9, 2011
+
+Added date input formats for input strings.
+
+### 0.4.0
+
+- Release May 9, 2011
+
+Added underscore.date to npm. Removed dependencies on underscore.
+
+### 0.3.2
+
+- Release Apr 9, 2011
+
+Added `'z'` and `'zz'` to `_.date().format()`. Cleaned up some redundant code to trim off some bytes.
+
+### 0.3.1
+
+- Release Mar 25, 2011
+
+Cleaned up the namespace. Moved all date manipulation and display functions to the _.date() object.
+
+### 0.3.0
+
+- Release Mar 25, 2011
+
+Switched to the Underscore methodology of not mucking with the native objects' prototypes.
+Made chaining possible.
+
+### 0.2.1
+
+- Release
+
+Changed date names to be a more pseudo standardized 'dddd, MMMM Do YYYY, h:mm:ss a'.
+Added `Date.prototype` functions `add`, `subtract`, `isdst`, and `isleapyear`.
+
+### 0.2.0
+
+- Release
+
+Changed function names to be more concise.
+Changed date format from php date format to custom format.
+
+### 0.1.0
+
+- Release
+
+Initial release
+

+ 22 - 0
node_modules/moment/LICENSE

@@ -0,0 +1,22 @@
+Copyright (c) JS Foundation and other contributors
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.

+ 55 - 0
node_modules/moment/README.md

@@ -0,0 +1,55 @@
+# [Moment.js](http://momentjs.com/)
+
+[![NPM version][npm-version-image]][npm-url]
+[![NPM downloads][npm-downloads-image]][npm-downloads-url]
+[![MIT License][license-image]][license-url]
+[![Build Status][travis-image]][travis-url]
+[![Coverage Status][coveralls-image]][coveralls-url]
+[![FOSSA Status][fossa-badge-image]][fossa-badge-url]
+[![SemVer compatibility][semver-image]][semver-url]
+
+A JavaScript date library for parsing, validating, manipulating, and formatting dates.
+
+## Project Status
+
+Moment.js is a legacy project, now in maintenance mode.  In most cases, you should choose a different library.
+
+For more details and recommendations, please see [Project Status](https://momentjs.com/docs/#/-project-status/) in the docs.
+
+*Thank you.*
+
+## Resources
+
+- [Documentation](https://momentjs.com/docs/)
+- [Changelog](CHANGELOG.md)
+- [Stack Overflow](https://stackoverflow.com/questions/tagged/momentjs)
+
+## License
+
+Moment.js is freely distributable under the terms of the [MIT license][license-url].
+
+[![FOSSA Status][fossa-large-image]][fossa-large-url]
+
+[license-image]: https://img.shields.io/badge/license-MIT-blue.svg?style=flat
+[license-url]: LICENSE
+
+[npm-url]: https://npmjs.org/package/moment
+[npm-version-image]: https://img.shields.io/npm/v/moment.svg?style=flat
+
+[npm-downloads-image]: https://img.shields.io/npm/dm/moment.svg?style=flat
+[npm-downloads-url]: https://npmcharts.com/compare/moment?minimal=true
+
+[travis-url]: https://travis-ci.org/moment/moment
+[travis-image]: https://img.shields.io/travis/moment/moment/develop.svg?style=flat
+
+[coveralls-image]: https://coveralls.io/repos/moment/moment/badge.svg?branch=develop
+[coveralls-url]: https://coveralls.io/r/moment/moment?branch=develop
+
+[fossa-badge-image]: https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmoment%2Fmoment.svg?type=shield
+[fossa-badge-url]: https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmoment%2Fmoment?ref=badge_shield
+
+[fossa-large-image]: https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmoment%2Fmoment.svg?type=large
+[fossa-large-url]: https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmoment%2Fmoment?ref=badge_large
+
+[semver-image]: https://api.dependabot.com/badges/compatibility_score?dependency-name=moment&package-manager=npm_and_yarn&version-scheme=semver
+[semver-url]: https://dependabot.com/compatibility-score.html?dependency-name=moment&package-manager=npm_and_yarn&version-scheme=semver

+ 71 - 0
node_modules/moment/dist/locale/af.js

@@ -0,0 +1,71 @@
+//! moment.js locale configuration
+//! locale : Afrikaans [af]
+//! author : Werner Mollentze : https://github.com/wernerm
+
+import moment from '../moment';
+
+export default moment.defineLocale('af', {
+    months: 'Januarie_Februarie_Maart_April_Mei_Junie_Julie_Augustus_September_Oktober_November_Desember'.split(
+        '_'
+    ),
+    monthsShort: 'Jan_Feb_Mrt_Apr_Mei_Jun_Jul_Aug_Sep_Okt_Nov_Des'.split('_'),
+    weekdays: 'Sondag_Maandag_Dinsdag_Woensdag_Donderdag_Vrydag_Saterdag'.split(
+        '_'
+    ),
+    weekdaysShort: 'Son_Maa_Din_Woe_Don_Vry_Sat'.split('_'),
+    weekdaysMin: 'So_Ma_Di_Wo_Do_Vr_Sa'.split('_'),
+    meridiemParse: /vm|nm/i,
+    isPM: function (input) {
+        return /^nm$/i.test(input);
+    },
+    meridiem: function (hours, minutes, isLower) {
+        if (hours < 12) {
+            return isLower ? 'vm' : 'VM';
+        } else {
+            return isLower ? 'nm' : 'NM';
+        }
+    },
+    longDateFormat: {
+        LT: 'HH:mm',
+        LTS: 'HH:mm:ss',
+        L: 'DD/MM/YYYY',
+        LL: 'D MMMM YYYY',
+        LLL: 'D MMMM YYYY HH:mm',
+        LLLL: 'dddd, D MMMM YYYY HH:mm',
+    },
+    calendar: {
+        sameDay: '[Vandag om] LT',
+        nextDay: '[Môre om] LT',
+        nextWeek: 'dddd [om] LT',
+        lastDay: '[Gister om] LT',
+        lastWeek: '[Laas] dddd [om] LT',
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: 'oor %s',
+        past: '%s gelede',
+        s: "'n paar sekondes",
+        ss: '%d sekondes',
+        m: "'n minuut",
+        mm: '%d minute',
+        h: "'n uur",
+        hh: '%d ure',
+        d: "'n dag",
+        dd: '%d dae',
+        M: "'n maand",
+        MM: '%d maande',
+        y: "'n jaar",
+        yy: '%d jaar',
+    },
+    dayOfMonthOrdinalParse: /\d{1,2}(ste|de)/,
+    ordinal: function (number) {
+        return (
+            number +
+            (number === 1 || number === 8 || number >= 20 ? 'ste' : 'de')
+        ); // Thanks to Joris Röling : https://github.com/jjupiter
+    },
+    week: {
+        dow: 1, // Maandag is die eerste dag van die week.
+        doy: 4, // Die week wat die 4de Januarie bevat is die eerste week van die jaar.
+    },
+});

+ 156 - 0
node_modules/moment/dist/locale/ar-dz.js

@@ -0,0 +1,156 @@
+//! moment.js locale configuration
+//! locale : Arabic (Algeria) [ar-dz]
+//! author : Amine Roukh: https://github.com/Amine27
+//! author : Abdel Said: https://github.com/abdelsaid
+//! author : Ahmed Elkhatib
+//! author : forabi https://github.com/forabi
+//! author : Noureddine LOUAHEDJ : https://github.com/noureddinem
+
+import moment from '../moment';
+
+var pluralForm = function (n) {
+        return n === 0
+            ? 0
+            : n === 1
+            ? 1
+            : n === 2
+            ? 2
+            : n % 100 >= 3 && n % 100 <= 10
+            ? 3
+            : n % 100 >= 11
+            ? 4
+            : 5;
+    },
+    plurals = {
+        s: [
+            'أقل من ثانية',
+            'ثانية واحدة',
+            ['ثانيتان', 'ثانيتين'],
+            '%d ثوان',
+            '%d ثانية',
+            '%d ثانية',
+        ],
+        m: [
+            'أقل من دقيقة',
+            'دقيقة واحدة',
+            ['دقيقتان', 'دقيقتين'],
+            '%d دقائق',
+            '%d دقيقة',
+            '%d دقيقة',
+        ],
+        h: [
+            'أقل من ساعة',
+            'ساعة واحدة',
+            ['ساعتان', 'ساعتين'],
+            '%d ساعات',
+            '%d ساعة',
+            '%d ساعة',
+        ],
+        d: [
+            'أقل من يوم',
+            'يوم واحد',
+            ['يومان', 'يومين'],
+            '%d أيام',
+            '%d يومًا',
+            '%d يوم',
+        ],
+        M: [
+            'أقل من شهر',
+            'شهر واحد',
+            ['شهران', 'شهرين'],
+            '%d أشهر',
+            '%d شهرا',
+            '%d شهر',
+        ],
+        y: [
+            'أقل من عام',
+            'عام واحد',
+            ['عامان', 'عامين'],
+            '%d أعوام',
+            '%d عامًا',
+            '%d عام',
+        ],
+    },
+    pluralize = function (u) {
+        return function (number, withoutSuffix, string, isFuture) {
+            var f = pluralForm(number),
+                str = plurals[u][pluralForm(number)];
+            if (f === 2) {
+                str = str[withoutSuffix ? 0 : 1];
+            }
+            return str.replace(/%d/i, number);
+        };
+    },
+    months = [
+        'جانفي',
+        'فيفري',
+        'مارس',
+        'أفريل',
+        'ماي',
+        'جوان',
+        'جويلية',
+        'أوت',
+        'سبتمبر',
+        'أكتوبر',
+        'نوفمبر',
+        'ديسمبر',
+    ];
+
+export default moment.defineLocale('ar-dz', {
+    months: months,
+    monthsShort: months,
+    weekdays: 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'),
+    weekdaysShort: 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'),
+    weekdaysMin: 'ح_ن_ث_ر_خ_ج_س'.split('_'),
+    weekdaysParseExact: true,
+    longDateFormat: {
+        LT: 'HH:mm',
+        LTS: 'HH:mm:ss',
+        L: 'D/\u200FM/\u200FYYYY',
+        LL: 'D MMMM YYYY',
+        LLL: 'D MMMM YYYY HH:mm',
+        LLLL: 'dddd D MMMM YYYY HH:mm',
+    },
+    meridiemParse: /ص|م/,
+    isPM: function (input) {
+        return 'م' === input;
+    },
+    meridiem: function (hour, minute, isLower) {
+        if (hour < 12) {
+            return 'ص';
+        } else {
+            return 'م';
+        }
+    },
+    calendar: {
+        sameDay: '[اليوم عند الساعة] LT',
+        nextDay: '[غدًا عند الساعة] LT',
+        nextWeek: 'dddd [عند الساعة] LT',
+        lastDay: '[أمس عند الساعة] LT',
+        lastWeek: 'dddd [عند الساعة] LT',
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: 'بعد %s',
+        past: 'منذ %s',
+        s: pluralize('s'),
+        ss: pluralize('s'),
+        m: pluralize('m'),
+        mm: pluralize('m'),
+        h: pluralize('h'),
+        hh: pluralize('h'),
+        d: pluralize('d'),
+        dd: pluralize('d'),
+        M: pluralize('M'),
+        MM: pluralize('M'),
+        y: pluralize('y'),
+        yy: pluralize('y'),
+    },
+    postformat: function (string) {
+        return string.replace(/,/g, '،');
+    },
+    week: {
+        dow: 0, // Sunday is the first day of the week.
+        doy: 4, // The week that contains Jan 4th is the first week of the year.
+    },
+});

+ 55 - 0
node_modules/moment/dist/locale/ar-kw.js

@@ -0,0 +1,55 @@
+//! moment.js locale configuration
+//! locale : Arabic (Kuwait) [ar-kw]
+//! author : Nusret Parlak: https://github.com/nusretparlak
+
+import moment from '../moment';
+
+export default moment.defineLocale('ar-kw', {
+    months: 'يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر'.split(
+        '_'
+    ),
+    monthsShort:
+        'يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر'.split(
+            '_'
+        ),
+    weekdays: 'الأحد_الإتنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'),
+    weekdaysShort: 'احد_اتنين_ثلاثاء_اربعاء_خميس_جمعة_سبت'.split('_'),
+    weekdaysMin: 'ح_ن_ث_ر_خ_ج_س'.split('_'),
+    weekdaysParseExact: true,
+    longDateFormat: {
+        LT: 'HH:mm',
+        LTS: 'HH:mm:ss',
+        L: 'DD/MM/YYYY',
+        LL: 'D MMMM YYYY',
+        LLL: 'D MMMM YYYY HH:mm',
+        LLLL: 'dddd D MMMM YYYY HH:mm',
+    },
+    calendar: {
+        sameDay: '[اليوم على الساعة] LT',
+        nextDay: '[غدا على الساعة] LT',
+        nextWeek: 'dddd [على الساعة] LT',
+        lastDay: '[أمس على الساعة] LT',
+        lastWeek: 'dddd [على الساعة] LT',
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: 'في %s',
+        past: 'منذ %s',
+        s: 'ثوان',
+        ss: '%d ثانية',
+        m: 'دقيقة',
+        mm: '%d دقائق',
+        h: 'ساعة',
+        hh: '%d ساعات',
+        d: 'يوم',
+        dd: '%d أيام',
+        M: 'شهر',
+        MM: '%d أشهر',
+        y: 'سنة',
+        yy: '%d سنوات',
+    },
+    week: {
+        dow: 0, // Sunday is the first day of the week.
+        doy: 12, // The week that contains Jan 12th is the first week of the year.
+    },
+});

+ 171 - 0
node_modules/moment/dist/locale/ar-ly.js

@@ -0,0 +1,171 @@
+//! moment.js locale configuration
+//! locale : Arabic (Libya) [ar-ly]
+//! author : Ali Hmer: https://github.com/kikoanis
+
+import moment from '../moment';
+
+var symbolMap = {
+        1: '1',
+        2: '2',
+        3: '3',
+        4: '4',
+        5: '5',
+        6: '6',
+        7: '7',
+        8: '8',
+        9: '9',
+        0: '0',
+    },
+    pluralForm = function (n) {
+        return n === 0
+            ? 0
+            : n === 1
+            ? 1
+            : n === 2
+            ? 2
+            : n % 100 >= 3 && n % 100 <= 10
+            ? 3
+            : n % 100 >= 11
+            ? 4
+            : 5;
+    },
+    plurals = {
+        s: [
+            'أقل من ثانية',
+            'ثانية واحدة',
+            ['ثانيتان', 'ثانيتين'],
+            '%d ثوان',
+            '%d ثانية',
+            '%d ثانية',
+        ],
+        m: [
+            'أقل من دقيقة',
+            'دقيقة واحدة',
+            ['دقيقتان', 'دقيقتين'],
+            '%d دقائق',
+            '%d دقيقة',
+            '%d دقيقة',
+        ],
+        h: [
+            'أقل من ساعة',
+            'ساعة واحدة',
+            ['ساعتان', 'ساعتين'],
+            '%d ساعات',
+            '%d ساعة',
+            '%d ساعة',
+        ],
+        d: [
+            'أقل من يوم',
+            'يوم واحد',
+            ['يومان', 'يومين'],
+            '%d أيام',
+            '%d يومًا',
+            '%d يوم',
+        ],
+        M: [
+            'أقل من شهر',
+            'شهر واحد',
+            ['شهران', 'شهرين'],
+            '%d أشهر',
+            '%d شهرا',
+            '%d شهر',
+        ],
+        y: [
+            'أقل من عام',
+            'عام واحد',
+            ['عامان', 'عامين'],
+            '%d أعوام',
+            '%d عامًا',
+            '%d عام',
+        ],
+    },
+    pluralize = function (u) {
+        return function (number, withoutSuffix, string, isFuture) {
+            var f = pluralForm(number),
+                str = plurals[u][pluralForm(number)];
+            if (f === 2) {
+                str = str[withoutSuffix ? 0 : 1];
+            }
+            return str.replace(/%d/i, number);
+        };
+    },
+    months = [
+        'يناير',
+        'فبراير',
+        'مارس',
+        'أبريل',
+        'مايو',
+        'يونيو',
+        'يوليو',
+        'أغسطس',
+        'سبتمبر',
+        'أكتوبر',
+        'نوفمبر',
+        'ديسمبر',
+    ];
+
+export default moment.defineLocale('ar-ly', {
+    months: months,
+    monthsShort: months,
+    weekdays: 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'),
+    weekdaysShort: 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'),
+    weekdaysMin: 'ح_ن_ث_ر_خ_ج_س'.split('_'),
+    weekdaysParseExact: true,
+    longDateFormat: {
+        LT: 'HH:mm',
+        LTS: 'HH:mm:ss',
+        L: 'D/\u200FM/\u200FYYYY',
+        LL: 'D MMMM YYYY',
+        LLL: 'D MMMM YYYY HH:mm',
+        LLLL: 'dddd D MMMM YYYY HH:mm',
+    },
+    meridiemParse: /ص|م/,
+    isPM: function (input) {
+        return 'م' === input;
+    },
+    meridiem: function (hour, minute, isLower) {
+        if (hour < 12) {
+            return 'ص';
+        } else {
+            return 'م';
+        }
+    },
+    calendar: {
+        sameDay: '[اليوم عند الساعة] LT',
+        nextDay: '[غدًا عند الساعة] LT',
+        nextWeek: 'dddd [عند الساعة] LT',
+        lastDay: '[أمس عند الساعة] LT',
+        lastWeek: 'dddd [عند الساعة] LT',
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: 'بعد %s',
+        past: 'منذ %s',
+        s: pluralize('s'),
+        ss: pluralize('s'),
+        m: pluralize('m'),
+        mm: pluralize('m'),
+        h: pluralize('h'),
+        hh: pluralize('h'),
+        d: pluralize('d'),
+        dd: pluralize('d'),
+        M: pluralize('M'),
+        MM: pluralize('M'),
+        y: pluralize('y'),
+        yy: pluralize('y'),
+    },
+    preparse: function (string) {
+        return string.replace(/،/g, ',');
+    },
+    postformat: function (string) {
+        return string
+            .replace(/\d/g, function (match) {
+                return symbolMap[match];
+            })
+            .replace(/,/g, '،');
+    },
+    week: {
+        dow: 6, // Saturday is the first day of the week.
+        doy: 12, // The week that contains Jan 12th is the first week of the year.
+    },
+});

+ 56 - 0
node_modules/moment/dist/locale/ar-ma.js

@@ -0,0 +1,56 @@
+//! moment.js locale configuration
+//! locale : Arabic (Morocco) [ar-ma]
+//! author : ElFadili Yassine : https://github.com/ElFadiliY
+//! author : Abdel Said : https://github.com/abdelsaid
+
+import moment from '../moment';
+
+export default moment.defineLocale('ar-ma', {
+    months: 'يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر'.split(
+        '_'
+    ),
+    monthsShort:
+        'يناير_فبراير_مارس_أبريل_ماي_يونيو_يوليوز_غشت_شتنبر_أكتوبر_نونبر_دجنبر'.split(
+            '_'
+        ),
+    weekdays: 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'),
+    weekdaysShort: 'احد_اثنين_ثلاثاء_اربعاء_خميس_جمعة_سبت'.split('_'),
+    weekdaysMin: 'ح_ن_ث_ر_خ_ج_س'.split('_'),
+    weekdaysParseExact: true,
+    longDateFormat: {
+        LT: 'HH:mm',
+        LTS: 'HH:mm:ss',
+        L: 'DD/MM/YYYY',
+        LL: 'D MMMM YYYY',
+        LLL: 'D MMMM YYYY HH:mm',
+        LLLL: 'dddd D MMMM YYYY HH:mm',
+    },
+    calendar: {
+        sameDay: '[اليوم على الساعة] LT',
+        nextDay: '[غدا على الساعة] LT',
+        nextWeek: 'dddd [على الساعة] LT',
+        lastDay: '[أمس على الساعة] LT',
+        lastWeek: 'dddd [على الساعة] LT',
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: 'في %s',
+        past: 'منذ %s',
+        s: 'ثوان',
+        ss: '%d ثانية',
+        m: 'دقيقة',
+        mm: '%d دقائق',
+        h: 'ساعة',
+        hh: '%d ساعات',
+        d: 'يوم',
+        dd: '%d أيام',
+        M: 'شهر',
+        MM: '%d أشهر',
+        y: 'سنة',
+        yy: '%d سنوات',
+    },
+    week: {
+        dow: 1, // Monday is the first day of the week.
+        doy: 4, // The week that contains Jan 4th is the first week of the year.
+    },
+});

+ 105 - 0
node_modules/moment/dist/locale/ar-sa.js

@@ -0,0 +1,105 @@
+//! moment.js locale configuration
+//! locale : Arabic (Saudi Arabia) [ar-sa]
+//! author : Suhail Alkowaileet : https://github.com/xsoh
+
+import moment from '../moment';
+
+var symbolMap = {
+        1: '١',
+        2: '٢',
+        3: '٣',
+        4: '٤',
+        5: '٥',
+        6: '٦',
+        7: '٧',
+        8: '٨',
+        9: '٩',
+        0: '٠',
+    },
+    numberMap = {
+        '١': '1',
+        '٢': '2',
+        '٣': '3',
+        '٤': '4',
+        '٥': '5',
+        '٦': '6',
+        '٧': '7',
+        '٨': '8',
+        '٩': '9',
+        '٠': '0',
+    };
+
+export default moment.defineLocale('ar-sa', {
+    months: 'يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split(
+        '_'
+    ),
+    monthsShort:
+        'يناير_فبراير_مارس_أبريل_مايو_يونيو_يوليو_أغسطس_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split(
+            '_'
+        ),
+    weekdays: 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'),
+    weekdaysShort: 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'),
+    weekdaysMin: 'ح_ن_ث_ر_خ_ج_س'.split('_'),
+    weekdaysParseExact: true,
+    longDateFormat: {
+        LT: 'HH:mm',
+        LTS: 'HH:mm:ss',
+        L: 'DD/MM/YYYY',
+        LL: 'D MMMM YYYY',
+        LLL: 'D MMMM YYYY HH:mm',
+        LLLL: 'dddd D MMMM YYYY HH:mm',
+    },
+    meridiemParse: /ص|م/,
+    isPM: function (input) {
+        return 'م' === input;
+    },
+    meridiem: function (hour, minute, isLower) {
+        if (hour < 12) {
+            return 'ص';
+        } else {
+            return 'م';
+        }
+    },
+    calendar: {
+        sameDay: '[اليوم على الساعة] LT',
+        nextDay: '[غدا على الساعة] LT',
+        nextWeek: 'dddd [على الساعة] LT',
+        lastDay: '[أمس على الساعة] LT',
+        lastWeek: 'dddd [على الساعة] LT',
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: 'في %s',
+        past: 'منذ %s',
+        s: 'ثوان',
+        ss: '%d ثانية',
+        m: 'دقيقة',
+        mm: '%d دقائق',
+        h: 'ساعة',
+        hh: '%d ساعات',
+        d: 'يوم',
+        dd: '%d أيام',
+        M: 'شهر',
+        MM: '%d أشهر',
+        y: 'سنة',
+        yy: '%d سنوات',
+    },
+    preparse: function (string) {
+        return string
+            .replace(/[١٢٣٤٥٦٧٨٩٠]/g, function (match) {
+                return numberMap[match];
+            })
+            .replace(/،/g, ',');
+    },
+    postformat: function (string) {
+        return string
+            .replace(/\d/g, function (match) {
+                return symbolMap[match];
+            })
+            .replace(/,/g, '،');
+    },
+    week: {
+        dow: 0, // Sunday is the first day of the week.
+        doy: 6, // The week that contains Jan 6th is the first week of the year.
+    },
+});

+ 55 - 0
node_modules/moment/dist/locale/ar-tn.js

@@ -0,0 +1,55 @@
+//! moment.js locale configuration
+//! locale  :  Arabic (Tunisia) [ar-tn]
+//! author : Nader Toukabri : https://github.com/naderio
+
+import moment from '../moment';
+
+export default moment.defineLocale('ar-tn', {
+    months: 'جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split(
+        '_'
+    ),
+    monthsShort:
+        'جانفي_فيفري_مارس_أفريل_ماي_جوان_جويلية_أوت_سبتمبر_أكتوبر_نوفمبر_ديسمبر'.split(
+            '_'
+        ),
+    weekdays: 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'),
+    weekdaysShort: 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'),
+    weekdaysMin: 'ح_ن_ث_ر_خ_ج_س'.split('_'),
+    weekdaysParseExact: true,
+    longDateFormat: {
+        LT: 'HH:mm',
+        LTS: 'HH:mm:ss',
+        L: 'DD/MM/YYYY',
+        LL: 'D MMMM YYYY',
+        LLL: 'D MMMM YYYY HH:mm',
+        LLLL: 'dddd D MMMM YYYY HH:mm',
+    },
+    calendar: {
+        sameDay: '[اليوم على الساعة] LT',
+        nextDay: '[غدا على الساعة] LT',
+        nextWeek: 'dddd [على الساعة] LT',
+        lastDay: '[أمس على الساعة] LT',
+        lastWeek: 'dddd [على الساعة] LT',
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: 'في %s',
+        past: 'منذ %s',
+        s: 'ثوان',
+        ss: '%d ثانية',
+        m: 'دقيقة',
+        mm: '%d دقائق',
+        h: 'ساعة',
+        hh: '%d ساعات',
+        d: 'يوم',
+        dd: '%d أيام',
+        M: 'شهر',
+        MM: '%d أشهر',
+        y: 'سنة',
+        yy: '%d سنوات',
+    },
+    week: {
+        dow: 1, // Monday is the first day of the week.
+        doy: 4, // The week that contains Jan 4th is the first week of the year.
+    },
+});

+ 189 - 0
node_modules/moment/dist/locale/ar.js

@@ -0,0 +1,189 @@
+//! moment.js locale configuration
+//! locale : Arabic [ar]
+//! author : Abdel Said: https://github.com/abdelsaid
+//! author : Ahmed Elkhatib
+//! author : forabi https://github.com/forabi
+
+import moment from '../moment';
+
+var symbolMap = {
+        1: '١',
+        2: '٢',
+        3: '٣',
+        4: '٤',
+        5: '٥',
+        6: '٦',
+        7: '٧',
+        8: '٨',
+        9: '٩',
+        0: '٠',
+    },
+    numberMap = {
+        '١': '1',
+        '٢': '2',
+        '٣': '3',
+        '٤': '4',
+        '٥': '5',
+        '٦': '6',
+        '٧': '7',
+        '٨': '8',
+        '٩': '9',
+        '٠': '0',
+    },
+    pluralForm = function (n) {
+        return n === 0
+            ? 0
+            : n === 1
+            ? 1
+            : n === 2
+            ? 2
+            : n % 100 >= 3 && n % 100 <= 10
+            ? 3
+            : n % 100 >= 11
+            ? 4
+            : 5;
+    },
+    plurals = {
+        s: [
+            'أقل من ثانية',
+            'ثانية واحدة',
+            ['ثانيتان', 'ثانيتين'],
+            '%d ثوان',
+            '%d ثانية',
+            '%d ثانية',
+        ],
+        m: [
+            'أقل من دقيقة',
+            'دقيقة واحدة',
+            ['دقيقتان', 'دقيقتين'],
+            '%d دقائق',
+            '%d دقيقة',
+            '%d دقيقة',
+        ],
+        h: [
+            'أقل من ساعة',
+            'ساعة واحدة',
+            ['ساعتان', 'ساعتين'],
+            '%d ساعات',
+            '%d ساعة',
+            '%d ساعة',
+        ],
+        d: [
+            'أقل من يوم',
+            'يوم واحد',
+            ['يومان', 'يومين'],
+            '%d أيام',
+            '%d يومًا',
+            '%d يوم',
+        ],
+        M: [
+            'أقل من شهر',
+            'شهر واحد',
+            ['شهران', 'شهرين'],
+            '%d أشهر',
+            '%d شهرا',
+            '%d شهر',
+        ],
+        y: [
+            'أقل من عام',
+            'عام واحد',
+            ['عامان', 'عامين'],
+            '%d أعوام',
+            '%d عامًا',
+            '%d عام',
+        ],
+    },
+    pluralize = function (u) {
+        return function (number, withoutSuffix, string, isFuture) {
+            var f = pluralForm(number),
+                str = plurals[u][pluralForm(number)];
+            if (f === 2) {
+                str = str[withoutSuffix ? 0 : 1];
+            }
+            return str.replace(/%d/i, number);
+        };
+    },
+    months = [
+        'يناير',
+        'فبراير',
+        'مارس',
+        'أبريل',
+        'مايو',
+        'يونيو',
+        'يوليو',
+        'أغسطس',
+        'سبتمبر',
+        'أكتوبر',
+        'نوفمبر',
+        'ديسمبر',
+    ];
+
+export default moment.defineLocale('ar', {
+    months: months,
+    monthsShort: months,
+    weekdays: 'الأحد_الإثنين_الثلاثاء_الأربعاء_الخميس_الجمعة_السبت'.split('_'),
+    weekdaysShort: 'أحد_إثنين_ثلاثاء_أربعاء_خميس_جمعة_سبت'.split('_'),
+    weekdaysMin: 'ح_ن_ث_ر_خ_ج_س'.split('_'),
+    weekdaysParseExact: true,
+    longDateFormat: {
+        LT: 'HH:mm',
+        LTS: 'HH:mm:ss',
+        L: 'D/\u200FM/\u200FYYYY',
+        LL: 'D MMMM YYYY',
+        LLL: 'D MMMM YYYY HH:mm',
+        LLLL: 'dddd D MMMM YYYY HH:mm',
+    },
+    meridiemParse: /ص|م/,
+    isPM: function (input) {
+        return 'م' === input;
+    },
+    meridiem: function (hour, minute, isLower) {
+        if (hour < 12) {
+            return 'ص';
+        } else {
+            return 'م';
+        }
+    },
+    calendar: {
+        sameDay: '[اليوم عند الساعة] LT',
+        nextDay: '[غدًا عند الساعة] LT',
+        nextWeek: 'dddd [عند الساعة] LT',
+        lastDay: '[أمس عند الساعة] LT',
+        lastWeek: 'dddd [عند الساعة] LT',
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: 'بعد %s',
+        past: 'منذ %s',
+        s: pluralize('s'),
+        ss: pluralize('s'),
+        m: pluralize('m'),
+        mm: pluralize('m'),
+        h: pluralize('h'),
+        hh: pluralize('h'),
+        d: pluralize('d'),
+        dd: pluralize('d'),
+        M: pluralize('M'),
+        MM: pluralize('M'),
+        y: pluralize('y'),
+        yy: pluralize('y'),
+    },
+    preparse: function (string) {
+        return string
+            .replace(/[١٢٣٤٥٦٧٨٩٠]/g, function (match) {
+                return numberMap[match];
+            })
+            .replace(/،/g, ',');
+    },
+    postformat: function (string) {
+        return string
+            .replace(/\d/g, function (match) {
+                return symbolMap[match];
+            })
+            .replace(/,/g, '،');
+    },
+    week: {
+        dow: 6, // Saturday is the first day of the week.
+        doy: 12, // The week that contains Jan 12th is the first week of the year.
+    },
+});

+ 102 - 0
node_modules/moment/dist/locale/az.js

@@ -0,0 +1,102 @@
+//! moment.js locale configuration
+//! locale : Azerbaijani [az]
+//! author : topchiyev : https://github.com/topchiyev
+
+import moment from '../moment';
+
+var suffixes = {
+    1: '-inci',
+    5: '-inci',
+    8: '-inci',
+    70: '-inci',
+    80: '-inci',
+    2: '-nci',
+    7: '-nci',
+    20: '-nci',
+    50: '-nci',
+    3: '-üncü',
+    4: '-üncü',
+    100: '-üncü',
+    6: '-ncı',
+    9: '-uncu',
+    10: '-uncu',
+    30: '-uncu',
+    60: '-ıncı',
+    90: '-ıncı',
+};
+
+export default moment.defineLocale('az', {
+    months: 'yanvar_fevral_mart_aprel_may_iyun_iyul_avqust_sentyabr_oktyabr_noyabr_dekabr'.split(
+        '_'
+    ),
+    monthsShort: 'yan_fev_mar_apr_may_iyn_iyl_avq_sen_okt_noy_dek'.split('_'),
+    weekdays:
+        'Bazar_Bazar ertəsi_Çərşənbə axşamı_Çərşənbə_Cümə axşamı_Cümə_Şənbə'.split(
+            '_'
+        ),
+    weekdaysShort: 'Baz_BzE_ÇAx_Çər_CAx_Cüm_Şən'.split('_'),
+    weekdaysMin: 'Bz_BE_ÇA_Çə_CA_Cü_Şə'.split('_'),
+    weekdaysParseExact: true,
+    longDateFormat: {
+        LT: 'HH:mm',
+        LTS: 'HH:mm:ss',
+        L: 'DD.MM.YYYY',
+        LL: 'D MMMM YYYY',
+        LLL: 'D MMMM YYYY HH:mm',
+        LLLL: 'dddd, D MMMM YYYY HH:mm',
+    },
+    calendar: {
+        sameDay: '[bugün saat] LT',
+        nextDay: '[sabah saat] LT',
+        nextWeek: '[gələn həftə] dddd [saat] LT',
+        lastDay: '[dünən] LT',
+        lastWeek: '[keçən həftə] dddd [saat] LT',
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: '%s sonra',
+        past: '%s əvvəl',
+        s: 'bir neçə saniyə',
+        ss: '%d saniyə',
+        m: 'bir dəqiqə',
+        mm: '%d dəqiqə',
+        h: 'bir saat',
+        hh: '%d saat',
+        d: 'bir gün',
+        dd: '%d gün',
+        M: 'bir ay',
+        MM: '%d ay',
+        y: 'bir il',
+        yy: '%d il',
+    },
+    meridiemParse: /gecə|səhər|gündüz|axşam/,
+    isPM: function (input) {
+        return /^(gündüz|axşam)$/.test(input);
+    },
+    meridiem: function (hour, minute, isLower) {
+        if (hour < 4) {
+            return 'gecə';
+        } else if (hour < 12) {
+            return 'səhər';
+        } else if (hour < 17) {
+            return 'gündüz';
+        } else {
+            return 'axşam';
+        }
+    },
+    dayOfMonthOrdinalParse: /\d{1,2}-(ıncı|inci|nci|üncü|ncı|uncu)/,
+    ordinal: function (number) {
+        if (number === 0) {
+            // special case for zero
+            return number + '-ıncı';
+        }
+        var a = number % 10,
+            b = (number % 100) - a,
+            c = number >= 100 ? 100 : null;
+        return number + (suffixes[a] || suffixes[b] || suffixes[c]);
+    },
+    week: {
+        dow: 1, // Monday is the first day of the week.
+        doy: 7, // The week that contains Jan 7th is the first week of the year.
+    },
+});

+ 142 - 0
node_modules/moment/dist/locale/be.js

@@ -0,0 +1,142 @@
+//! moment.js locale configuration
+//! locale : Belarusian [be]
+//! author : Dmitry Demidov : https://github.com/demidov91
+//! author: Praleska: http://praleska.pro/
+//! Author : Menelion Elensúle : https://github.com/Oire
+
+import moment from '../moment';
+
+function plural(word, num) {
+    var forms = word.split('_');
+    return num % 10 === 1 && num % 100 !== 11
+        ? forms[0]
+        : num % 10 >= 2 && num % 10 <= 4 && (num % 100 < 10 || num % 100 >= 20)
+        ? forms[1]
+        : forms[2];
+}
+function relativeTimeWithPlural(number, withoutSuffix, key) {
+    var format = {
+        ss: withoutSuffix ? 'секунда_секунды_секунд' : 'секунду_секунды_секунд',
+        mm: withoutSuffix ? 'хвіліна_хвіліны_хвілін' : 'хвіліну_хвіліны_хвілін',
+        hh: withoutSuffix ? 'гадзіна_гадзіны_гадзін' : 'гадзіну_гадзіны_гадзін',
+        dd: 'дзень_дні_дзён',
+        MM: 'месяц_месяцы_месяцаў',
+        yy: 'год_гады_гадоў',
+    };
+    if (key === 'm') {
+        return withoutSuffix ? 'хвіліна' : 'хвіліну';
+    } else if (key === 'h') {
+        return withoutSuffix ? 'гадзіна' : 'гадзіну';
+    } else {
+        return number + ' ' + plural(format[key], +number);
+    }
+}
+
+export default moment.defineLocale('be', {
+    months: {
+        format: 'студзеня_лютага_сакавіка_красавіка_траўня_чэрвеня_ліпеня_жніўня_верасня_кастрычніка_лістапада_снежня'.split(
+            '_'
+        ),
+        standalone:
+            'студзень_люты_сакавік_красавік_травень_чэрвень_ліпень_жнівень_верасень_кастрычнік_лістапад_снежань'.split(
+                '_'
+            ),
+    },
+    monthsShort:
+        'студ_лют_сак_крас_трав_чэрв_ліп_жнів_вер_каст_ліст_снеж'.split('_'),
+    weekdays: {
+        format: 'нядзелю_панядзелак_аўторак_сераду_чацвер_пятніцу_суботу'.split(
+            '_'
+        ),
+        standalone:
+            'нядзеля_панядзелак_аўторак_серада_чацвер_пятніца_субота'.split(
+                '_'
+            ),
+        isFormat: /\[ ?[Ууў] ?(?:мінулую|наступную)? ?\] ?dddd/,
+    },
+    weekdaysShort: 'нд_пн_ат_ср_чц_пт_сб'.split('_'),
+    weekdaysMin: 'нд_пн_ат_ср_чц_пт_сб'.split('_'),
+    longDateFormat: {
+        LT: 'HH:mm',
+        LTS: 'HH:mm:ss',
+        L: 'DD.MM.YYYY',
+        LL: 'D MMMM YYYY г.',
+        LLL: 'D MMMM YYYY г., HH:mm',
+        LLLL: 'dddd, D MMMM YYYY г., HH:mm',
+    },
+    calendar: {
+        sameDay: '[Сёння ў] LT',
+        nextDay: '[Заўтра ў] LT',
+        lastDay: '[Учора ў] LT',
+        nextWeek: function () {
+            return '[У] dddd [ў] LT';
+        },
+        lastWeek: function () {
+            switch (this.day()) {
+                case 0:
+                case 3:
+                case 5:
+                case 6:
+                    return '[У мінулую] dddd [ў] LT';
+                case 1:
+                case 2:
+                case 4:
+                    return '[У мінулы] dddd [ў] LT';
+            }
+        },
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: 'праз %s',
+        past: '%s таму',
+        s: 'некалькі секунд',
+        m: relativeTimeWithPlural,
+        mm: relativeTimeWithPlural,
+        h: relativeTimeWithPlural,
+        hh: relativeTimeWithPlural,
+        d: 'дзень',
+        dd: relativeTimeWithPlural,
+        M: 'месяц',
+        MM: relativeTimeWithPlural,
+        y: 'год',
+        yy: relativeTimeWithPlural,
+    },
+    meridiemParse: /ночы|раніцы|дня|вечара/,
+    isPM: function (input) {
+        return /^(дня|вечара)$/.test(input);
+    },
+    meridiem: function (hour, minute, isLower) {
+        if (hour < 4) {
+            return 'ночы';
+        } else if (hour < 12) {
+            return 'раніцы';
+        } else if (hour < 17) {
+            return 'дня';
+        } else {
+            return 'вечара';
+        }
+    },
+    dayOfMonthOrdinalParse: /\d{1,2}-(і|ы|га)/,
+    ordinal: function (number, period) {
+        switch (period) {
+            case 'M':
+            case 'd':
+            case 'DDD':
+            case 'w':
+            case 'W':
+                return (number % 10 === 2 || number % 10 === 3) &&
+                    number % 100 !== 12 &&
+                    number % 100 !== 13
+                    ? number + '-і'
+                    : number + '-ы';
+            case 'D':
+                return number + '-га';
+            default:
+                return number;
+        }
+    },
+    week: {
+        dow: 1, // Monday is the first day of the week.
+        doy: 7, // The week that contains Jan 7th is the first week of the year.
+    },
+});

+ 87 - 0
node_modules/moment/dist/locale/bg.js

@@ -0,0 +1,87 @@
+//! moment.js locale configuration
+//! locale : Bulgarian [bg]
+//! author : Krasen Borisov : https://github.com/kraz
+
+import moment from '../moment';
+
+export default moment.defineLocale('bg', {
+    months: 'януари_февруари_март_април_май_юни_юли_август_септември_октомври_ноември_декември'.split(
+        '_'
+    ),
+    monthsShort: 'яну_фев_мар_апр_май_юни_юли_авг_сеп_окт_ное_дек'.split('_'),
+    weekdays: 'неделя_понеделник_вторник_сряда_четвъртък_петък_събота'.split(
+        '_'
+    ),
+    weekdaysShort: 'нед_пон_вто_сря_чет_пет_съб'.split('_'),
+    weekdaysMin: 'нд_пн_вт_ср_чт_пт_сб'.split('_'),
+    longDateFormat: {
+        LT: 'H:mm',
+        LTS: 'H:mm:ss',
+        L: 'D.MM.YYYY',
+        LL: 'D MMMM YYYY',
+        LLL: 'D MMMM YYYY H:mm',
+        LLLL: 'dddd, D MMMM YYYY H:mm',
+    },
+    calendar: {
+        sameDay: '[Днес в] LT',
+        nextDay: '[Утре в] LT',
+        nextWeek: 'dddd [в] LT',
+        lastDay: '[Вчера в] LT',
+        lastWeek: function () {
+            switch (this.day()) {
+                case 0:
+                case 3:
+                case 6:
+                    return '[Миналата] dddd [в] LT';
+                case 1:
+                case 2:
+                case 4:
+                case 5:
+                    return '[Миналия] dddd [в] LT';
+            }
+        },
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: 'след %s',
+        past: 'преди %s',
+        s: 'няколко секунди',
+        ss: '%d секунди',
+        m: 'минута',
+        mm: '%d минути',
+        h: 'час',
+        hh: '%d часа',
+        d: 'ден',
+        dd: '%d дена',
+        w: 'седмица',
+        ww: '%d седмици',
+        M: 'месец',
+        MM: '%d месеца',
+        y: 'година',
+        yy: '%d години',
+    },
+    dayOfMonthOrdinalParse: /\d{1,2}-(ев|ен|ти|ви|ри|ми)/,
+    ordinal: function (number) {
+        var lastDigit = number % 10,
+            last2Digits = number % 100;
+        if (number === 0) {
+            return number + '-ев';
+        } else if (last2Digits === 0) {
+            return number + '-ен';
+        } else if (last2Digits > 10 && last2Digits < 20) {
+            return number + '-ти';
+        } else if (lastDigit === 1) {
+            return number + '-ви';
+        } else if (lastDigit === 2) {
+            return number + '-ри';
+        } else if (lastDigit === 7 || lastDigit === 8) {
+            return number + '-ми';
+        } else {
+            return number + '-ти';
+        }
+    },
+    week: {
+        dow: 1, // Monday is the first day of the week.
+        doy: 7, // The week that contains Jan 7th is the first week of the year.
+    },
+});

+ 52 - 0
node_modules/moment/dist/locale/bm.js

@@ -0,0 +1,52 @@
+//! moment.js locale configuration
+//! locale : Bambara [bm]
+//! author : Estelle Comment : https://github.com/estellecomment
+// Language contact person : Abdoufata Kane : https://github.com/abdoufata
+
+import moment from '../moment';
+
+export default moment.defineLocale('bm', {
+    months: 'Zanwuyekalo_Fewuruyekalo_Marisikalo_Awirilikalo_Mɛkalo_Zuwɛnkalo_Zuluyekalo_Utikalo_Sɛtanburukalo_ɔkutɔburukalo_Nowanburukalo_Desanburukalo'.split(
+        '_'
+    ),
+    monthsShort: 'Zan_Few_Mar_Awi_Mɛ_Zuw_Zul_Uti_Sɛt_ɔku_Now_Des'.split('_'),
+    weekdays: 'Kari_Ntɛnɛn_Tarata_Araba_Alamisa_Juma_Sibiri'.split('_'),
+    weekdaysShort: 'Kar_Ntɛ_Tar_Ara_Ala_Jum_Sib'.split('_'),
+    weekdaysMin: 'Ka_Nt_Ta_Ar_Al_Ju_Si'.split('_'),
+    longDateFormat: {
+        LT: 'HH:mm',
+        LTS: 'HH:mm:ss',
+        L: 'DD/MM/YYYY',
+        LL: 'MMMM [tile] D [san] YYYY',
+        LLL: 'MMMM [tile] D [san] YYYY [lɛrɛ] HH:mm',
+        LLLL: 'dddd MMMM [tile] D [san] YYYY [lɛrɛ] HH:mm',
+    },
+    calendar: {
+        sameDay: '[Bi lɛrɛ] LT',
+        nextDay: '[Sini lɛrɛ] LT',
+        nextWeek: 'dddd [don lɛrɛ] LT',
+        lastDay: '[Kunu lɛrɛ] LT',
+        lastWeek: 'dddd [tɛmɛnen lɛrɛ] LT',
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: '%s kɔnɔ',
+        past: 'a bɛ %s bɔ',
+        s: 'sanga dama dama',
+        ss: 'sekondi %d',
+        m: 'miniti kelen',
+        mm: 'miniti %d',
+        h: 'lɛrɛ kelen',
+        hh: 'lɛrɛ %d',
+        d: 'tile kelen',
+        dd: 'tile %d',
+        M: 'kalo kelen',
+        MM: 'kalo %d',
+        y: 'san kelen',
+        yy: 'san %d',
+    },
+    week: {
+        dow: 1, // Monday is the first day of the week.
+        doy: 4, // The week that contains Jan 4th is the first week of the year.
+    },
+});

+ 129 - 0
node_modules/moment/dist/locale/bn-bd.js

@@ -0,0 +1,129 @@
+//! moment.js locale configuration
+//! locale : Bengali (Bangladesh) [bn-bd]
+//! author : Asraf Hossain Patoary : https://github.com/ashwoolford
+
+import moment from '../moment';
+
+var symbolMap = {
+        1: '১',
+        2: '২',
+        3: '৩',
+        4: '৪',
+        5: '৫',
+        6: '৬',
+        7: '৭',
+        8: '৮',
+        9: '৯',
+        0: '০',
+    },
+    numberMap = {
+        '১': '1',
+        '২': '2',
+        '৩': '3',
+        '৪': '4',
+        '৫': '5',
+        '৬': '6',
+        '৭': '7',
+        '৮': '8',
+        '৯': '9',
+        '০': '0',
+    };
+
+export default moment.defineLocale('bn-bd', {
+    months: 'জানুয়ারি_ফেব্রুয়ারি_মার্চ_এপ্রিল_মে_জুন_জুলাই_আগস্ট_সেপ্টেম্বর_অক্টোবর_নভেম্বর_ডিসেম্বর'.split(
+        '_'
+    ),
+    monthsShort:
+        'জানু_ফেব্রু_মার্চ_এপ্রিল_মে_জুন_জুলাই_আগস্ট_সেপ্ট_অক্টো_নভে_ডিসে'.split(
+            '_'
+        ),
+    weekdays: 'রবিবার_সোমবার_মঙ্গলবার_বুধবার_বৃহস্পতিবার_শুক্রবার_শনিবার'.split(
+        '_'
+    ),
+    weekdaysShort: 'রবি_সোম_মঙ্গল_বুধ_বৃহস্পতি_শুক্র_শনি'.split('_'),
+    weekdaysMin: 'রবি_সোম_মঙ্গল_বুধ_বৃহ_শুক্র_শনি'.split('_'),
+    longDateFormat: {
+        LT: 'A h:mm সময়',
+        LTS: 'A h:mm:ss সময়',
+        L: 'DD/MM/YYYY',
+        LL: 'D MMMM YYYY',
+        LLL: 'D MMMM YYYY, A h:mm সময়',
+        LLLL: 'dddd, D MMMM YYYY, A h:mm সময়',
+    },
+    calendar: {
+        sameDay: '[আজ] LT',
+        nextDay: '[আগামীকাল] LT',
+        nextWeek: 'dddd, LT',
+        lastDay: '[গতকাল] LT',
+        lastWeek: '[গত] dddd, LT',
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: '%s পরে',
+        past: '%s আগে',
+        s: 'কয়েক সেকেন্ড',
+        ss: '%d সেকেন্ড',
+        m: 'এক মিনিট',
+        mm: '%d মিনিট',
+        h: 'এক ঘন্টা',
+        hh: '%d ঘন্টা',
+        d: 'এক দিন',
+        dd: '%d দিন',
+        M: 'এক মাস',
+        MM: '%d মাস',
+        y: 'এক বছর',
+        yy: '%d বছর',
+    },
+    preparse: function (string) {
+        return string.replace(/[১২৩৪৫৬৭৮৯০]/g, function (match) {
+            return numberMap[match];
+        });
+    },
+    postformat: function (string) {
+        return string.replace(/\d/g, function (match) {
+            return symbolMap[match];
+        });
+    },
+
+    meridiemParse: /রাত|ভোর|সকাল|দুপুর|বিকাল|সন্ধ্যা|রাত/,
+    meridiemHour: function (hour, meridiem) {
+        if (hour === 12) {
+            hour = 0;
+        }
+        if (meridiem === 'রাত') {
+            return hour < 4 ? hour : hour + 12;
+        } else if (meridiem === 'ভোর') {
+            return hour;
+        } else if (meridiem === 'সকাল') {
+            return hour;
+        } else if (meridiem === 'দুপুর') {
+            return hour >= 3 ? hour : hour + 12;
+        } else if (meridiem === 'বিকাল') {
+            return hour + 12;
+        } else if (meridiem === 'সন্ধ্যা') {
+            return hour + 12;
+        }
+    },
+
+    meridiem: function (hour, minute, isLower) {
+        if (hour < 4) {
+            return 'রাত';
+        } else if (hour < 6) {
+            return 'ভোর';
+        } else if (hour < 12) {
+            return 'সকাল';
+        } else if (hour < 15) {
+            return 'দুপুর';
+        } else if (hour < 18) {
+            return 'বিকাল';
+        } else if (hour < 20) {
+            return 'সন্ধ্যা';
+        } else {
+            return 'রাত';
+        }
+    },
+    week: {
+        dow: 0, // Sunday is the first day of the week.
+        doy: 6, // The week that contains Jan 6th is the first week of the year.
+    },
+});

+ 119 - 0
node_modules/moment/dist/locale/bn.js

@@ -0,0 +1,119 @@
+//! moment.js locale configuration
+//! locale : Bengali [bn]
+//! author : Kaushik Gandhi : https://github.com/kaushikgandhi
+
+import moment from '../moment';
+
+var symbolMap = {
+        1: '১',
+        2: '২',
+        3: '৩',
+        4: '৪',
+        5: '৫',
+        6: '৬',
+        7: '৭',
+        8: '৮',
+        9: '৯',
+        0: '০',
+    },
+    numberMap = {
+        '১': '1',
+        '২': '2',
+        '৩': '3',
+        '৪': '4',
+        '৫': '5',
+        '৬': '6',
+        '৭': '7',
+        '৮': '8',
+        '৯': '9',
+        '০': '0',
+    };
+
+export default moment.defineLocale('bn', {
+    months: 'জানুয়ারি_ফেব্রুয়ারি_মার্চ_এপ্রিল_মে_জুন_জুলাই_আগস্ট_সেপ্টেম্বর_অক্টোবর_নভেম্বর_ডিসেম্বর'.split(
+        '_'
+    ),
+    monthsShort:
+        'জানু_ফেব্রু_মার্চ_এপ্রিল_মে_জুন_জুলাই_আগস্ট_সেপ্ট_অক্টো_নভে_ডিসে'.split(
+            '_'
+        ),
+    weekdays: 'রবিবার_সোমবার_মঙ্গলবার_বুধবার_বৃহস্পতিবার_শুক্রবার_শনিবার'.split(
+        '_'
+    ),
+    weekdaysShort: 'রবি_সোম_মঙ্গল_বুধ_বৃহস্পতি_শুক্র_শনি'.split('_'),
+    weekdaysMin: 'রবি_সোম_মঙ্গল_বুধ_বৃহ_শুক্র_শনি'.split('_'),
+    longDateFormat: {
+        LT: 'A h:mm সময়',
+        LTS: 'A h:mm:ss সময়',
+        L: 'DD/MM/YYYY',
+        LL: 'D MMMM YYYY',
+        LLL: 'D MMMM YYYY, A h:mm সময়',
+        LLLL: 'dddd, D MMMM YYYY, A h:mm সময়',
+    },
+    calendar: {
+        sameDay: '[আজ] LT',
+        nextDay: '[আগামীকাল] LT',
+        nextWeek: 'dddd, LT',
+        lastDay: '[গতকাল] LT',
+        lastWeek: '[গত] dddd, LT',
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: '%s পরে',
+        past: '%s আগে',
+        s: 'কয়েক সেকেন্ড',
+        ss: '%d সেকেন্ড',
+        m: 'এক মিনিট',
+        mm: '%d মিনিট',
+        h: 'এক ঘন্টা',
+        hh: '%d ঘন্টা',
+        d: 'এক দিন',
+        dd: '%d দিন',
+        M: 'এক মাস',
+        MM: '%d মাস',
+        y: 'এক বছর',
+        yy: '%d বছর',
+    },
+    preparse: function (string) {
+        return string.replace(/[১২৩৪৫৬৭৮৯০]/g, function (match) {
+            return numberMap[match];
+        });
+    },
+    postformat: function (string) {
+        return string.replace(/\d/g, function (match) {
+            return symbolMap[match];
+        });
+    },
+    meridiemParse: /রাত|সকাল|দুপুর|বিকাল|রাত/,
+    meridiemHour: function (hour, meridiem) {
+        if (hour === 12) {
+            hour = 0;
+        }
+        if (
+            (meridiem === 'রাত' && hour >= 4) ||
+            (meridiem === 'দুপুর' && hour < 5) ||
+            meridiem === 'বিকাল'
+        ) {
+            return hour + 12;
+        } else {
+            return hour;
+        }
+    },
+    meridiem: function (hour, minute, isLower) {
+        if (hour < 4) {
+            return 'রাত';
+        } else if (hour < 10) {
+            return 'সকাল';
+        } else if (hour < 17) {
+            return 'দুপুর';
+        } else if (hour < 20) {
+            return 'বিকাল';
+        } else {
+            return 'রাত';
+        }
+    },
+    week: {
+        dow: 0, // Sunday is the first day of the week.
+        doy: 6, // The week that contains Jan 6th is the first week of the year.
+    },
+});

+ 124 - 0
node_modules/moment/dist/locale/bo.js

@@ -0,0 +1,124 @@
+//! moment.js locale configuration
+//! locale : Tibetan [bo]
+//! author : Thupten N. Chakrishar : https://github.com/vajradog
+
+import moment from '../moment';
+
+var symbolMap = {
+        1: '༡',
+        2: '༢',
+        3: '༣',
+        4: '༤',
+        5: '༥',
+        6: '༦',
+        7: '༧',
+        8: '༨',
+        9: '༩',
+        0: '༠',
+    },
+    numberMap = {
+        '༡': '1',
+        '༢': '2',
+        '༣': '3',
+        '༤': '4',
+        '༥': '5',
+        '༦': '6',
+        '༧': '7',
+        '༨': '8',
+        '༩': '9',
+        '༠': '0',
+    };
+
+export default moment.defineLocale('bo', {
+    months: 'ཟླ་བ་དང་པོ_ཟླ་བ་གཉིས་པ_ཟླ་བ་གསུམ་པ_ཟླ་བ་བཞི་པ_ཟླ་བ་ལྔ་པ_ཟླ་བ་དྲུག་པ_ཟླ་བ་བདུན་པ_ཟླ་བ་བརྒྱད་པ_ཟླ་བ་དགུ་པ_ཟླ་བ་བཅུ་པ_ཟླ་བ་བཅུ་གཅིག་པ_ཟླ་བ་བཅུ་གཉིས་པ'.split(
+        '_'
+    ),
+    monthsShort:
+        'ཟླ་1_ཟླ་2_ཟླ་3_ཟླ་4_ཟླ་5_ཟླ་6_ཟླ་7_ཟླ་8_ཟླ་9_ཟླ་10_ཟླ་11_ཟླ་12'.split(
+            '_'
+        ),
+    monthsShortRegex: /^(ཟླ་\d{1,2})/,
+    monthsParseExact: true,
+    weekdays:
+        'གཟའ་ཉི་མ་_གཟའ་ཟླ་བ་_གཟའ་མིག་དམར་_གཟའ་ལྷག་པ་_གཟའ་ཕུར་བུ_གཟའ་པ་སངས་_གཟའ་སྤེན་པ་'.split(
+            '_'
+        ),
+    weekdaysShort: 'ཉི་མ་_ཟླ་བ་_མིག་དམར་_ལྷག་པ་_ཕུར་བུ_པ་སངས་_སྤེན་པ་'.split(
+        '_'
+    ),
+    weekdaysMin: 'ཉི_ཟླ_མིག_ལྷག_ཕུར_སངས_སྤེན'.split('_'),
+    longDateFormat: {
+        LT: 'A h:mm',
+        LTS: 'A h:mm:ss',
+        L: 'DD/MM/YYYY',
+        LL: 'D MMMM YYYY',
+        LLL: 'D MMMM YYYY, A h:mm',
+        LLLL: 'dddd, D MMMM YYYY, A h:mm',
+    },
+    calendar: {
+        sameDay: '[དི་རིང] LT',
+        nextDay: '[སང་ཉིན] LT',
+        nextWeek: '[བདུན་ཕྲག་རྗེས་མ], LT',
+        lastDay: '[ཁ་སང] LT',
+        lastWeek: '[བདུན་ཕྲག་མཐའ་མ] dddd, LT',
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: '%s ལ་',
+        past: '%s སྔན་ལ',
+        s: 'ལམ་སང',
+        ss: '%d སྐར་ཆ།',
+        m: 'སྐར་མ་གཅིག',
+        mm: '%d སྐར་མ',
+        h: 'ཆུ་ཚོད་གཅིག',
+        hh: '%d ཆུ་ཚོད',
+        d: 'ཉིན་གཅིག',
+        dd: '%d ཉིན་',
+        M: 'ཟླ་བ་གཅིག',
+        MM: '%d ཟླ་བ',
+        y: 'ལོ་གཅིག',
+        yy: '%d ལོ',
+    },
+    preparse: function (string) {
+        return string.replace(/[༡༢༣༤༥༦༧༨༩༠]/g, function (match) {
+            return numberMap[match];
+        });
+    },
+    postformat: function (string) {
+        return string.replace(/\d/g, function (match) {
+            return symbolMap[match];
+        });
+    },
+    meridiemParse: /མཚན་མོ|ཞོགས་ཀས|ཉིན་གུང|དགོང་དག|མཚན་མོ/,
+    meridiemHour: function (hour, meridiem) {
+        if (hour === 12) {
+            hour = 0;
+        }
+        if (
+            (meridiem === 'མཚན་མོ' && hour >= 4) ||
+            (meridiem === 'ཉིན་གུང' && hour < 5) ||
+            meridiem === 'དགོང་དག'
+        ) {
+            return hour + 12;
+        } else {
+            return hour;
+        }
+    },
+    meridiem: function (hour, minute, isLower) {
+        if (hour < 4) {
+            return 'མཚན་མོ';
+        } else if (hour < 10) {
+            return 'ཞོགས་ཀས';
+        } else if (hour < 17) {
+            return 'ཉིན་གུང';
+        } else if (hour < 20) {
+            return 'དགོང་དག';
+        } else {
+            return 'མཚན་མོ';
+        }
+    },
+    week: {
+        dow: 0, // Sunday is the first day of the week.
+        doy: 6, // The week that contains Jan 6th is the first week of the year.
+    },
+});

+ 168 - 0
node_modules/moment/dist/locale/br.js

@@ -0,0 +1,168 @@
+//! moment.js locale configuration
+//! locale : Breton [br]
+//! author : Jean-Baptiste Le Duigou : https://github.com/jbleduigou
+
+import moment from '../moment';
+
+function relativeTimeWithMutation(number, withoutSuffix, key) {
+    var format = {
+        mm: 'munutenn',
+        MM: 'miz',
+        dd: 'devezh',
+    };
+    return number + ' ' + mutation(format[key], number);
+}
+function specialMutationForYears(number) {
+    switch (lastNumber(number)) {
+        case 1:
+        case 3:
+        case 4:
+        case 5:
+        case 9:
+            return number + ' bloaz';
+        default:
+            return number + ' vloaz';
+    }
+}
+function lastNumber(number) {
+    if (number > 9) {
+        return lastNumber(number % 10);
+    }
+    return number;
+}
+function mutation(text, number) {
+    if (number === 2) {
+        return softMutation(text);
+    }
+    return text;
+}
+function softMutation(text) {
+    var mutationTable = {
+        m: 'v',
+        b: 'v',
+        d: 'z',
+    };
+    if (mutationTable[text.charAt(0)] === undefined) {
+        return text;
+    }
+    return mutationTable[text.charAt(0)] + text.substring(1);
+}
+
+var monthsParse = [
+        /^gen/i,
+        /^c[ʼ\']hwe/i,
+        /^meu/i,
+        /^ebr/i,
+        /^mae/i,
+        /^(mez|eve)/i,
+        /^gou/i,
+        /^eos/i,
+        /^gwe/i,
+        /^her/i,
+        /^du/i,
+        /^ker/i,
+    ],
+    monthsRegex =
+        /^(genver|c[ʼ\']hwevrer|meurzh|ebrel|mae|mezheven|gouere|eost|gwengolo|here|du|kerzu|gen|c[ʼ\']hwe|meu|ebr|mae|eve|gou|eos|gwe|her|du|ker)/i,
+    monthsStrictRegex =
+        /^(genver|c[ʼ\']hwevrer|meurzh|ebrel|mae|mezheven|gouere|eost|gwengolo|here|du|kerzu)/i,
+    monthsShortStrictRegex =
+        /^(gen|c[ʼ\']hwe|meu|ebr|mae|eve|gou|eos|gwe|her|du|ker)/i,
+    fullWeekdaysParse = [
+        /^sul/i,
+        /^lun/i,
+        /^meurzh/i,
+        /^merc[ʼ\']her/i,
+        /^yaou/i,
+        /^gwener/i,
+        /^sadorn/i,
+    ],
+    shortWeekdaysParse = [
+        /^Sul/i,
+        /^Lun/i,
+        /^Meu/i,
+        /^Mer/i,
+        /^Yao/i,
+        /^Gwe/i,
+        /^Sad/i,
+    ],
+    minWeekdaysParse = [
+        /^Su/i,
+        /^Lu/i,
+        /^Me([^r]|$)/i,
+        /^Mer/i,
+        /^Ya/i,
+        /^Gw/i,
+        /^Sa/i,
+    ];
+
+export default moment.defineLocale('br', {
+    months: 'Genver_Cʼhwevrer_Meurzh_Ebrel_Mae_Mezheven_Gouere_Eost_Gwengolo_Here_Du_Kerzu'.split(
+        '_'
+    ),
+    monthsShort: 'Gen_Cʼhwe_Meu_Ebr_Mae_Eve_Gou_Eos_Gwe_Her_Du_Ker'.split('_'),
+    weekdays: 'Sul_Lun_Meurzh_Mercʼher_Yaou_Gwener_Sadorn'.split('_'),
+    weekdaysShort: 'Sul_Lun_Meu_Mer_Yao_Gwe_Sad'.split('_'),
+    weekdaysMin: 'Su_Lu_Me_Mer_Ya_Gw_Sa'.split('_'),
+    weekdaysParse: minWeekdaysParse,
+    fullWeekdaysParse: fullWeekdaysParse,
+    shortWeekdaysParse: shortWeekdaysParse,
+    minWeekdaysParse: minWeekdaysParse,
+
+    monthsRegex: monthsRegex,
+    monthsShortRegex: monthsRegex,
+    monthsStrictRegex: monthsStrictRegex,
+    monthsShortStrictRegex: monthsShortStrictRegex,
+    monthsParse: monthsParse,
+    longMonthsParse: monthsParse,
+    shortMonthsParse: monthsParse,
+
+    longDateFormat: {
+        LT: 'HH:mm',
+        LTS: 'HH:mm:ss',
+        L: 'DD/MM/YYYY',
+        LL: 'D [a viz] MMMM YYYY',
+        LLL: 'D [a viz] MMMM YYYY HH:mm',
+        LLLL: 'dddd, D [a viz] MMMM YYYY HH:mm',
+    },
+    calendar: {
+        sameDay: '[Hiziv da] LT',
+        nextDay: '[Warcʼhoazh da] LT',
+        nextWeek: 'dddd [da] LT',
+        lastDay: '[Decʼh da] LT',
+        lastWeek: 'dddd [paset da] LT',
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: 'a-benn %s',
+        past: '%s ʼzo',
+        s: 'un nebeud segondennoù',
+        ss: '%d eilenn',
+        m: 'ur vunutenn',
+        mm: relativeTimeWithMutation,
+        h: 'un eur',
+        hh: '%d eur',
+        d: 'un devezh',
+        dd: relativeTimeWithMutation,
+        M: 'ur miz',
+        MM: relativeTimeWithMutation,
+        y: 'ur bloaz',
+        yy: specialMutationForYears,
+    },
+    dayOfMonthOrdinalParse: /\d{1,2}(añ|vet)/,
+    ordinal: function (number) {
+        var output = number === 1 ? 'añ' : 'vet';
+        return number + output;
+    },
+    week: {
+        dow: 1, // Monday is the first day of the week.
+        doy: 4, // The week that contains Jan 4th is the first week of the year.
+    },
+    meridiemParse: /a.m.|g.m./, // goude merenn | a-raok merenn
+    isPM: function (token) {
+        return token === 'g.m.';
+    },
+    meridiem: function (hour, minute, isLower) {
+        return hour < 12 ? 'a.m.' : 'g.m.';
+    },
+});

+ 150 - 0
node_modules/moment/dist/locale/bs.js

@@ -0,0 +1,150 @@
+//! moment.js locale configuration
+//! locale : Bosnian [bs]
+//! author : Nedim Cholich : https://github.com/frontyard
+//! based on (hr) translation by Bojan Marković
+
+import moment from '../moment';
+
+function translate(number, withoutSuffix, key) {
+    var result = number + ' ';
+    switch (key) {
+        case 'ss':
+            if (number === 1) {
+                result += 'sekunda';
+            } else if (number === 2 || number === 3 || number === 4) {
+                result += 'sekunde';
+            } else {
+                result += 'sekundi';
+            }
+            return result;
+        case 'm':
+            return withoutSuffix ? 'jedna minuta' : 'jedne minute';
+        case 'mm':
+            if (number === 1) {
+                result += 'minuta';
+            } else if (number === 2 || number === 3 || number === 4) {
+                result += 'minute';
+            } else {
+                result += 'minuta';
+            }
+            return result;
+        case 'h':
+            return withoutSuffix ? 'jedan sat' : 'jednog sata';
+        case 'hh':
+            if (number === 1) {
+                result += 'sat';
+            } else if (number === 2 || number === 3 || number === 4) {
+                result += 'sata';
+            } else {
+                result += 'sati';
+            }
+            return result;
+        case 'dd':
+            if (number === 1) {
+                result += 'dan';
+            } else {
+                result += 'dana';
+            }
+            return result;
+        case 'MM':
+            if (number === 1) {
+                result += 'mjesec';
+            } else if (number === 2 || number === 3 || number === 4) {
+                result += 'mjeseca';
+            } else {
+                result += 'mjeseci';
+            }
+            return result;
+        case 'yy':
+            if (number === 1) {
+                result += 'godina';
+            } else if (number === 2 || number === 3 || number === 4) {
+                result += 'godine';
+            } else {
+                result += 'godina';
+            }
+            return result;
+    }
+}
+
+export default moment.defineLocale('bs', {
+    months: 'januar_februar_mart_april_maj_juni_juli_august_septembar_oktobar_novembar_decembar'.split(
+        '_'
+    ),
+    monthsShort:
+        'jan._feb._mar._apr._maj._jun._jul._aug._sep._okt._nov._dec.'.split(
+            '_'
+        ),
+    monthsParseExact: true,
+    weekdays: 'nedjelja_ponedjeljak_utorak_srijeda_četvrtak_petak_subota'.split(
+        '_'
+    ),
+    weekdaysShort: 'ned._pon._uto._sri._čet._pet._sub.'.split('_'),
+    weekdaysMin: 'ne_po_ut_sr_če_pe_su'.split('_'),
+    weekdaysParseExact: true,
+    longDateFormat: {
+        LT: 'H:mm',
+        LTS: 'H:mm:ss',
+        L: 'DD.MM.YYYY',
+        LL: 'D. MMMM YYYY',
+        LLL: 'D. MMMM YYYY H:mm',
+        LLLL: 'dddd, D. MMMM YYYY H:mm',
+    },
+    calendar: {
+        sameDay: '[danas u] LT',
+        nextDay: '[sutra u] LT',
+        nextWeek: function () {
+            switch (this.day()) {
+                case 0:
+                    return '[u] [nedjelju] [u] LT';
+                case 3:
+                    return '[u] [srijedu] [u] LT';
+                case 6:
+                    return '[u] [subotu] [u] LT';
+                case 1:
+                case 2:
+                case 4:
+                case 5:
+                    return '[u] dddd [u] LT';
+            }
+        },
+        lastDay: '[jučer u] LT',
+        lastWeek: function () {
+            switch (this.day()) {
+                case 0:
+                case 3:
+                    return '[prošlu] dddd [u] LT';
+                case 6:
+                    return '[prošle] [subote] [u] LT';
+                case 1:
+                case 2:
+                case 4:
+                case 5:
+                    return '[prošli] dddd [u] LT';
+            }
+        },
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: 'za %s',
+        past: 'prije %s',
+        s: 'par sekundi',
+        ss: translate,
+        m: translate,
+        mm: translate,
+        h: translate,
+        hh: translate,
+        d: 'dan',
+        dd: translate,
+        M: 'mjesec',
+        MM: translate,
+        y: 'godinu',
+        yy: translate,
+    },
+    dayOfMonthOrdinalParse: /\d{1,2}\./,
+    ordinal: '%d.',
+    week: {
+        dow: 1, // Monday is the first day of the week.
+        doy: 7, // The week that contains Jan 7th is the first week of the year.
+    },
+});

+ 100 - 0
node_modules/moment/dist/locale/ca.js

@@ -0,0 +1,100 @@
+//! moment.js locale configuration
+//! locale : Catalan [ca]
+//! author : Juan G. Hurtado : https://github.com/juanghurtado
+
+import moment from '../moment';
+
+export default moment.defineLocale('ca', {
+    months: {
+        standalone:
+            'gener_febrer_març_abril_maig_juny_juliol_agost_setembre_octubre_novembre_desembre'.split(
+                '_'
+            ),
+        format: "de gener_de febrer_de març_d'abril_de maig_de juny_de juliol_d'agost_de setembre_d'octubre_de novembre_de desembre".split(
+            '_'
+        ),
+        isFormat: /D[oD]?(\s)+MMMM/,
+    },
+    monthsShort:
+        'gen._febr._març_abr._maig_juny_jul._ag._set._oct._nov._des.'.split(
+            '_'
+        ),
+    monthsParseExact: true,
+    weekdays:
+        'diumenge_dilluns_dimarts_dimecres_dijous_divendres_dissabte'.split(
+            '_'
+        ),
+    weekdaysShort: 'dg._dl._dt._dc._dj._dv._ds.'.split('_'),
+    weekdaysMin: 'dg_dl_dt_dc_dj_dv_ds'.split('_'),
+    weekdaysParseExact: true,
+    longDateFormat: {
+        LT: 'H:mm',
+        LTS: 'H:mm:ss',
+        L: 'DD/MM/YYYY',
+        LL: 'D MMMM [de] YYYY',
+        ll: 'D MMM YYYY',
+        LLL: 'D MMMM [de] YYYY [a les] H:mm',
+        lll: 'D MMM YYYY, H:mm',
+        LLLL: 'dddd D MMMM [de] YYYY [a les] H:mm',
+        llll: 'ddd D MMM YYYY, H:mm',
+    },
+    calendar: {
+        sameDay: function () {
+            return '[avui a ' + (this.hours() !== 1 ? 'les' : 'la') + '] LT';
+        },
+        nextDay: function () {
+            return '[demà a ' + (this.hours() !== 1 ? 'les' : 'la') + '] LT';
+        },
+        nextWeek: function () {
+            return 'dddd [a ' + (this.hours() !== 1 ? 'les' : 'la') + '] LT';
+        },
+        lastDay: function () {
+            return '[ahir a ' + (this.hours() !== 1 ? 'les' : 'la') + '] LT';
+        },
+        lastWeek: function () {
+            return (
+                '[el] dddd [passat a ' +
+                (this.hours() !== 1 ? 'les' : 'la') +
+                '] LT'
+            );
+        },
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: "d'aquí %s",
+        past: 'fa %s',
+        s: 'uns segons',
+        ss: '%d segons',
+        m: 'un minut',
+        mm: '%d minuts',
+        h: 'una hora',
+        hh: '%d hores',
+        d: 'un dia',
+        dd: '%d dies',
+        M: 'un mes',
+        MM: '%d mesos',
+        y: 'un any',
+        yy: '%d anys',
+    },
+    dayOfMonthOrdinalParse: /\d{1,2}(r|n|t|è|a)/,
+    ordinal: function (number, period) {
+        var output =
+            number === 1
+                ? 'r'
+                : number === 2
+                ? 'n'
+                : number === 3
+                ? 'r'
+                : number === 4
+                ? 't'
+                : 'è';
+        if (period === 'w' || period === 'W') {
+            output = 'a';
+        }
+        return number + output;
+    },
+    week: {
+        dow: 1, // Monday is the first day of the week.
+        doy: 4, // The week that contains Jan 4th is the first week of the year.
+    },
+});

+ 180 - 0
node_modules/moment/dist/locale/cs.js

@@ -0,0 +1,180 @@
+//! moment.js locale configuration
+//! locale : Czech [cs]
+//! author : petrbela : https://github.com/petrbela
+
+import moment from '../moment';
+
+var months = {
+        format: 'leden_únor_březen_duben_květen_červen_červenec_srpen_září_říjen_listopad_prosinec'.split(
+            '_'
+        ),
+        standalone:
+            'ledna_února_března_dubna_května_června_července_srpna_září_října_listopadu_prosince'.split(
+                '_'
+            ),
+    },
+    monthsShort = 'led_úno_bře_dub_kvě_čvn_čvc_srp_zář_říj_lis_pro'.split('_'),
+    monthsParse = [
+        /^led/i,
+        /^úno/i,
+        /^bře/i,
+        /^dub/i,
+        /^kvě/i,
+        /^(čvn|červen$|června)/i,
+        /^(čvc|červenec|července)/i,
+        /^srp/i,
+        /^zář/i,
+        /^říj/i,
+        /^lis/i,
+        /^pro/i,
+    ],
+    // NOTE: 'červen' is substring of 'červenec'; therefore 'červenec' must precede 'červen' in the regex to be fully matched.
+    // Otherwise parser matches '1. červenec' as '1. červen' + 'ec'.
+    monthsRegex =
+        /^(leden|únor|březen|duben|květen|červenec|července|červen|června|srpen|září|říjen|listopad|prosinec|led|úno|bře|dub|kvě|čvn|čvc|srp|zář|říj|lis|pro)/i;
+
+function plural(n) {
+    return n > 1 && n < 5 && ~~(n / 10) !== 1;
+}
+function translate(number, withoutSuffix, key, isFuture) {
+    var result = number + ' ';
+    switch (key) {
+        case 's': // a few seconds / in a few seconds / a few seconds ago
+            return withoutSuffix || isFuture ? 'pár sekund' : 'pár sekundami';
+        case 'ss': // 9 seconds / in 9 seconds / 9 seconds ago
+            if (withoutSuffix || isFuture) {
+                return result + (plural(number) ? 'sekundy' : 'sekund');
+            } else {
+                return result + 'sekundami';
+            }
+        case 'm': // a minute / in a minute / a minute ago
+            return withoutSuffix ? 'minuta' : isFuture ? 'minutu' : 'minutou';
+        case 'mm': // 9 minutes / in 9 minutes / 9 minutes ago
+            if (withoutSuffix || isFuture) {
+                return result + (plural(number) ? 'minuty' : 'minut');
+            } else {
+                return result + 'minutami';
+            }
+        case 'h': // an hour / in an hour / an hour ago
+            return withoutSuffix ? 'hodina' : isFuture ? 'hodinu' : 'hodinou';
+        case 'hh': // 9 hours / in 9 hours / 9 hours ago
+            if (withoutSuffix || isFuture) {
+                return result + (plural(number) ? 'hodiny' : 'hodin');
+            } else {
+                return result + 'hodinami';
+            }
+        case 'd': // a day / in a day / a day ago
+            return withoutSuffix || isFuture ? 'den' : 'dnem';
+        case 'dd': // 9 days / in 9 days / 9 days ago
+            if (withoutSuffix || isFuture) {
+                return result + (plural(number) ? 'dny' : 'dní');
+            } else {
+                return result + 'dny';
+            }
+        case 'M': // a month / in a month / a month ago
+            return withoutSuffix || isFuture ? 'měsíc' : 'měsícem';
+        case 'MM': // 9 months / in 9 months / 9 months ago
+            if (withoutSuffix || isFuture) {
+                return result + (plural(number) ? 'měsíce' : 'měsíců');
+            } else {
+                return result + 'měsíci';
+            }
+        case 'y': // a year / in a year / a year ago
+            return withoutSuffix || isFuture ? 'rok' : 'rokem';
+        case 'yy': // 9 years / in 9 years / 9 years ago
+            if (withoutSuffix || isFuture) {
+                return result + (plural(number) ? 'roky' : 'let');
+            } else {
+                return result + 'lety';
+            }
+    }
+}
+
+export default moment.defineLocale('cs', {
+    months: months,
+    monthsShort: monthsShort,
+    monthsRegex: monthsRegex,
+    monthsShortRegex: monthsRegex,
+    // NOTE: 'červen' is substring of 'červenec'; therefore 'červenec' must precede 'červen' in the regex to be fully matched.
+    // Otherwise parser matches '1. červenec' as '1. červen' + 'ec'.
+    monthsStrictRegex:
+        /^(leden|ledna|února|únor|březen|března|duben|dubna|květen|května|červenec|července|červen|června|srpen|srpna|září|říjen|října|listopadu|listopad|prosinec|prosince)/i,
+    monthsShortStrictRegex:
+        /^(led|úno|bře|dub|kvě|čvn|čvc|srp|zář|říj|lis|pro)/i,
+    monthsParse: monthsParse,
+    longMonthsParse: monthsParse,
+    shortMonthsParse: monthsParse,
+    weekdays: 'neděle_pondělí_úterý_středa_čtvrtek_pátek_sobota'.split('_'),
+    weekdaysShort: 'ne_po_út_st_čt_pá_so'.split('_'),
+    weekdaysMin: 'ne_po_út_st_čt_pá_so'.split('_'),
+    longDateFormat: {
+        LT: 'H:mm',
+        LTS: 'H:mm:ss',
+        L: 'DD.MM.YYYY',
+        LL: 'D. MMMM YYYY',
+        LLL: 'D. MMMM YYYY H:mm',
+        LLLL: 'dddd D. MMMM YYYY H:mm',
+        l: 'D. M. YYYY',
+    },
+    calendar: {
+        sameDay: '[dnes v] LT',
+        nextDay: '[zítra v] LT',
+        nextWeek: function () {
+            switch (this.day()) {
+                case 0:
+                    return '[v neděli v] LT';
+                case 1:
+                case 2:
+                    return '[v] dddd [v] LT';
+                case 3:
+                    return '[ve středu v] LT';
+                case 4:
+                    return '[ve čtvrtek v] LT';
+                case 5:
+                    return '[v pátek v] LT';
+                case 6:
+                    return '[v sobotu v] LT';
+            }
+        },
+        lastDay: '[včera v] LT',
+        lastWeek: function () {
+            switch (this.day()) {
+                case 0:
+                    return '[minulou neděli v] LT';
+                case 1:
+                case 2:
+                    return '[minulé] dddd [v] LT';
+                case 3:
+                    return '[minulou středu v] LT';
+                case 4:
+                case 5:
+                    return '[minulý] dddd [v] LT';
+                case 6:
+                    return '[minulou sobotu v] LT';
+            }
+        },
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: 'za %s',
+        past: 'před %s',
+        s: translate,
+        ss: translate,
+        m: translate,
+        mm: translate,
+        h: translate,
+        hh: translate,
+        d: translate,
+        dd: translate,
+        M: translate,
+        MM: translate,
+        y: translate,
+        yy: translate,
+    },
+    dayOfMonthOrdinalParse: /\d{1,2}\./,
+    ordinal: '%d.',
+    week: {
+        dow: 1, // Monday is the first day of the week.
+        doy: 4, // The week that contains Jan 4th is the first week of the year.
+    },
+});

+ 63 - 0
node_modules/moment/dist/locale/cv.js

@@ -0,0 +1,63 @@
+//! moment.js locale configuration
+//! locale : Chuvash [cv]
+//! author : Anatoly Mironov : https://github.com/mirontoli
+
+import moment from '../moment';
+
+export default moment.defineLocale('cv', {
+    months: 'кӑрлач_нарӑс_пуш_ака_май_ҫӗртме_утӑ_ҫурла_авӑн_юпа_чӳк_раштав'.split(
+        '_'
+    ),
+    monthsShort: 'кӑр_нар_пуш_ака_май_ҫӗр_утӑ_ҫур_авн_юпа_чӳк_раш'.split('_'),
+    weekdays:
+        'вырсарникун_тунтикун_ытларикун_юнкун_кӗҫнерникун_эрнекун_шӑматкун'.split(
+            '_'
+        ),
+    weekdaysShort: 'выр_тун_ытл_юн_кӗҫ_эрн_шӑм'.split('_'),
+    weekdaysMin: 'вр_тн_ыт_юн_кҫ_эр_шм'.split('_'),
+    longDateFormat: {
+        LT: 'HH:mm',
+        LTS: 'HH:mm:ss',
+        L: 'DD-MM-YYYY',
+        LL: 'YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ]',
+        LLL: 'YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm',
+        LLLL: 'dddd, YYYY [ҫулхи] MMMM [уйӑхӗн] D[-мӗшӗ], HH:mm',
+    },
+    calendar: {
+        sameDay: '[Паян] LT [сехетре]',
+        nextDay: '[Ыран] LT [сехетре]',
+        lastDay: '[Ӗнер] LT [сехетре]',
+        nextWeek: '[Ҫитес] dddd LT [сехетре]',
+        lastWeek: '[Иртнӗ] dddd LT [сехетре]',
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: function (output) {
+            var affix = /сехет$/i.exec(output)
+                ? 'рен'
+                : /ҫул$/i.exec(output)
+                ? 'тан'
+                : 'ран';
+            return output + affix;
+        },
+        past: '%s каялла',
+        s: 'пӗр-ик ҫеккунт',
+        ss: '%d ҫеккунт',
+        m: 'пӗр минут',
+        mm: '%d минут',
+        h: 'пӗр сехет',
+        hh: '%d сехет',
+        d: 'пӗр кун',
+        dd: '%d кун',
+        M: 'пӗр уйӑх',
+        MM: '%d уйӑх',
+        y: 'пӗр ҫул',
+        yy: '%d ҫул',
+    },
+    dayOfMonthOrdinalParse: /\d{1,2}-мӗш/,
+    ordinal: '%d-мӗш',
+    week: {
+        dow: 1, // Monday is the first day of the week.
+        doy: 7, // The week that contains Jan 7th is the first week of the year.
+    },
+});

+ 98 - 0
node_modules/moment/dist/locale/cy.js

@@ -0,0 +1,98 @@
+//! moment.js locale configuration
+//! locale : Welsh [cy]
+//! author : Robert Allen : https://github.com/robgallen
+//! author : https://github.com/ryangreaves
+
+import moment from '../moment';
+
+export default moment.defineLocale('cy', {
+    months: 'Ionawr_Chwefror_Mawrth_Ebrill_Mai_Mehefin_Gorffennaf_Awst_Medi_Hydref_Tachwedd_Rhagfyr'.split(
+        '_'
+    ),
+    monthsShort: 'Ion_Chwe_Maw_Ebr_Mai_Meh_Gor_Aws_Med_Hyd_Tach_Rhag'.split(
+        '_'
+    ),
+    weekdays:
+        'Dydd Sul_Dydd Llun_Dydd Mawrth_Dydd Mercher_Dydd Iau_Dydd Gwener_Dydd Sadwrn'.split(
+            '_'
+        ),
+    weekdaysShort: 'Sul_Llun_Maw_Mer_Iau_Gwe_Sad'.split('_'),
+    weekdaysMin: 'Su_Ll_Ma_Me_Ia_Gw_Sa'.split('_'),
+    weekdaysParseExact: true,
+    // time formats are the same as en-gb
+    longDateFormat: {
+        LT: 'HH:mm',
+        LTS: 'HH:mm:ss',
+        L: 'DD/MM/YYYY',
+        LL: 'D MMMM YYYY',
+        LLL: 'D MMMM YYYY HH:mm',
+        LLLL: 'dddd, D MMMM YYYY HH:mm',
+    },
+    calendar: {
+        sameDay: '[Heddiw am] LT',
+        nextDay: '[Yfory am] LT',
+        nextWeek: 'dddd [am] LT',
+        lastDay: '[Ddoe am] LT',
+        lastWeek: 'dddd [diwethaf am] LT',
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: 'mewn %s',
+        past: '%s yn ôl',
+        s: 'ychydig eiliadau',
+        ss: '%d eiliad',
+        m: 'munud',
+        mm: '%d munud',
+        h: 'awr',
+        hh: '%d awr',
+        d: 'diwrnod',
+        dd: '%d diwrnod',
+        M: 'mis',
+        MM: '%d mis',
+        y: 'blwyddyn',
+        yy: '%d flynedd',
+    },
+    dayOfMonthOrdinalParse: /\d{1,2}(fed|ain|af|il|ydd|ed|eg)/,
+    // traditional ordinal numbers above 31 are not commonly used in colloquial Welsh
+    ordinal: function (number) {
+        var b = number,
+            output = '',
+            lookup = [
+                '',
+                'af',
+                'il',
+                'ydd',
+                'ydd',
+                'ed',
+                'ed',
+                'ed',
+                'fed',
+                'fed',
+                'fed', // 1af to 10fed
+                'eg',
+                'fed',
+                'eg',
+                'eg',
+                'fed',
+                'eg',
+                'eg',
+                'fed',
+                'eg',
+                'fed', // 11eg to 20fed
+            ];
+        if (b > 20) {
+            if (b === 40 || b === 50 || b === 60 || b === 80 || b === 100) {
+                output = 'fed'; // not 30ain, 70ain or 90ain
+            } else {
+                output = 'ain';
+            }
+        } else if (b > 0) {
+            output = lookup[b];
+        }
+        return number + output;
+    },
+    week: {
+        dow: 1, // Monday is the first day of the week.
+        doy: 4, // The week that contains Jan 4th is the first week of the year.
+    },
+});

+ 53 - 0
node_modules/moment/dist/locale/da.js

@@ -0,0 +1,53 @@
+//! moment.js locale configuration
+//! locale : Danish [da]
+//! author : Ulrik Nielsen : https://github.com/mrbase
+
+import moment from '../moment';
+
+export default moment.defineLocale('da', {
+    months: 'januar_februar_marts_april_maj_juni_juli_august_september_oktober_november_december'.split(
+        '_'
+    ),
+    monthsShort: 'jan_feb_mar_apr_maj_jun_jul_aug_sep_okt_nov_dec'.split('_'),
+    weekdays: 'søndag_mandag_tirsdag_onsdag_torsdag_fredag_lørdag'.split('_'),
+    weekdaysShort: 'søn_man_tir_ons_tor_fre_lør'.split('_'),
+    weekdaysMin: 'sø_ma_ti_on_to_fr_lø'.split('_'),
+    longDateFormat: {
+        LT: 'HH:mm',
+        LTS: 'HH:mm:ss',
+        L: 'DD.MM.YYYY',
+        LL: 'D. MMMM YYYY',
+        LLL: 'D. MMMM YYYY HH:mm',
+        LLLL: 'dddd [d.] D. MMMM YYYY [kl.] HH:mm',
+    },
+    calendar: {
+        sameDay: '[i dag kl.] LT',
+        nextDay: '[i morgen kl.] LT',
+        nextWeek: 'på dddd [kl.] LT',
+        lastDay: '[i går kl.] LT',
+        lastWeek: '[i] dddd[s kl.] LT',
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: 'om %s',
+        past: '%s siden',
+        s: 'få sekunder',
+        ss: '%d sekunder',
+        m: 'et minut',
+        mm: '%d minutter',
+        h: 'en time',
+        hh: '%d timer',
+        d: 'en dag',
+        dd: '%d dage',
+        M: 'en måned',
+        MM: '%d måneder',
+        y: 'et år',
+        yy: '%d år',
+    },
+    dayOfMonthOrdinalParse: /\d{1,2}\./,
+    ordinal: '%d.',
+    week: {
+        dow: 1, // Monday is the first day of the week.
+        doy: 4, // The week that contains Jan 4th is the first week of the year.
+    },
+});

+ 79 - 0
node_modules/moment/dist/locale/de-at.js

@@ -0,0 +1,79 @@
+//! moment.js locale configuration
+//! locale : German (Austria) [de-at]
+//! author : lluchs : https://github.com/lluchs
+//! author: Menelion Elensúle: https://github.com/Oire
+//! author : Martin Groller : https://github.com/MadMG
+//! author : Mikolaj Dadela : https://github.com/mik01aj
+
+import moment from '../moment';
+
+function processRelativeTime(number, withoutSuffix, key, isFuture) {
+    var format = {
+        m: ['eine Minute', 'einer Minute'],
+        h: ['eine Stunde', 'einer Stunde'],
+        d: ['ein Tag', 'einem Tag'],
+        dd: [number + ' Tage', number + ' Tagen'],
+        w: ['eine Woche', 'einer Woche'],
+        M: ['ein Monat', 'einem Monat'],
+        MM: [number + ' Monate', number + ' Monaten'],
+        y: ['ein Jahr', 'einem Jahr'],
+        yy: [number + ' Jahre', number + ' Jahren'],
+    };
+    return withoutSuffix ? format[key][0] : format[key][1];
+}
+
+export default moment.defineLocale('de-at', {
+    months: 'Jänner_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember'.split(
+        '_'
+    ),
+    monthsShort:
+        'Jän._Feb._März_Apr._Mai_Juni_Juli_Aug._Sep._Okt._Nov._Dez.'.split('_'),
+    monthsParseExact: true,
+    weekdays:
+        'Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag'.split(
+            '_'
+        ),
+    weekdaysShort: 'So._Mo._Di._Mi._Do._Fr._Sa.'.split('_'),
+    weekdaysMin: 'So_Mo_Di_Mi_Do_Fr_Sa'.split('_'),
+    weekdaysParseExact: true,
+    longDateFormat: {
+        LT: 'HH:mm',
+        LTS: 'HH:mm:ss',
+        L: 'DD.MM.YYYY',
+        LL: 'D. MMMM YYYY',
+        LLL: 'D. MMMM YYYY HH:mm',
+        LLLL: 'dddd, D. MMMM YYYY HH:mm',
+    },
+    calendar: {
+        sameDay: '[heute um] LT [Uhr]',
+        sameElse: 'L',
+        nextDay: '[morgen um] LT [Uhr]',
+        nextWeek: 'dddd [um] LT [Uhr]',
+        lastDay: '[gestern um] LT [Uhr]',
+        lastWeek: '[letzten] dddd [um] LT [Uhr]',
+    },
+    relativeTime: {
+        future: 'in %s',
+        past: 'vor %s',
+        s: 'ein paar Sekunden',
+        ss: '%d Sekunden',
+        m: processRelativeTime,
+        mm: '%d Minuten',
+        h: processRelativeTime,
+        hh: '%d Stunden',
+        d: processRelativeTime,
+        dd: processRelativeTime,
+        w: processRelativeTime,
+        ww: '%d Wochen',
+        M: processRelativeTime,
+        MM: processRelativeTime,
+        y: processRelativeTime,
+        yy: processRelativeTime,
+    },
+    dayOfMonthOrdinalParse: /\d{1,2}\./,
+    ordinal: '%d.',
+    week: {
+        dow: 1, // Monday is the first day of the week.
+        doy: 4, // The week that contains Jan 4th is the first week of the year.
+    },
+});

+ 78 - 0
node_modules/moment/dist/locale/de-ch.js

@@ -0,0 +1,78 @@
+//! moment.js locale configuration
+//! locale : German (Switzerland) [de-ch]
+//! author : sschueller : https://github.com/sschueller
+
+// based on: https://www.bk.admin.ch/dokumentation/sprachen/04915/05016/index.html?lang=de#
+
+import moment from '../moment';
+
+function processRelativeTime(number, withoutSuffix, key, isFuture) {
+    var format = {
+        m: ['eine Minute', 'einer Minute'],
+        h: ['eine Stunde', 'einer Stunde'],
+        d: ['ein Tag', 'einem Tag'],
+        dd: [number + ' Tage', number + ' Tagen'],
+        w: ['eine Woche', 'einer Woche'],
+        M: ['ein Monat', 'einem Monat'],
+        MM: [number + ' Monate', number + ' Monaten'],
+        y: ['ein Jahr', 'einem Jahr'],
+        yy: [number + ' Jahre', number + ' Jahren'],
+    };
+    return withoutSuffix ? format[key][0] : format[key][1];
+}
+
+export default moment.defineLocale('de-ch', {
+    months: 'Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember'.split(
+        '_'
+    ),
+    monthsShort:
+        'Jan._Feb._März_Apr._Mai_Juni_Juli_Aug._Sep._Okt._Nov._Dez.'.split('_'),
+    monthsParseExact: true,
+    weekdays:
+        'Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag'.split(
+            '_'
+        ),
+    weekdaysShort: 'So_Mo_Di_Mi_Do_Fr_Sa'.split('_'),
+    weekdaysMin: 'So_Mo_Di_Mi_Do_Fr_Sa'.split('_'),
+    weekdaysParseExact: true,
+    longDateFormat: {
+        LT: 'HH:mm',
+        LTS: 'HH:mm:ss',
+        L: 'DD.MM.YYYY',
+        LL: 'D. MMMM YYYY',
+        LLL: 'D. MMMM YYYY HH:mm',
+        LLLL: 'dddd, D. MMMM YYYY HH:mm',
+    },
+    calendar: {
+        sameDay: '[heute um] LT [Uhr]',
+        sameElse: 'L',
+        nextDay: '[morgen um] LT [Uhr]',
+        nextWeek: 'dddd [um] LT [Uhr]',
+        lastDay: '[gestern um] LT [Uhr]',
+        lastWeek: '[letzten] dddd [um] LT [Uhr]',
+    },
+    relativeTime: {
+        future: 'in %s',
+        past: 'vor %s',
+        s: 'ein paar Sekunden',
+        ss: '%d Sekunden',
+        m: processRelativeTime,
+        mm: '%d Minuten',
+        h: processRelativeTime,
+        hh: '%d Stunden',
+        d: processRelativeTime,
+        dd: processRelativeTime,
+        w: processRelativeTime,
+        ww: '%d Wochen',
+        M: processRelativeTime,
+        MM: processRelativeTime,
+        y: processRelativeTime,
+        yy: processRelativeTime,
+    },
+    dayOfMonthOrdinalParse: /\d{1,2}\./,
+    ordinal: '%d.',
+    week: {
+        dow: 1, // Monday is the first day of the week.
+        doy: 4, // The week that contains Jan 4th is the first week of the year.
+    },
+});

+ 78 - 0
node_modules/moment/dist/locale/de.js

@@ -0,0 +1,78 @@
+//! moment.js locale configuration
+//! locale : German [de]
+//! author : lluchs : https://github.com/lluchs
+//! author: Menelion Elensúle: https://github.com/Oire
+//! author : Mikolaj Dadela : https://github.com/mik01aj
+
+import moment from '../moment';
+
+function processRelativeTime(number, withoutSuffix, key, isFuture) {
+    var format = {
+        m: ['eine Minute', 'einer Minute'],
+        h: ['eine Stunde', 'einer Stunde'],
+        d: ['ein Tag', 'einem Tag'],
+        dd: [number + ' Tage', number + ' Tagen'],
+        w: ['eine Woche', 'einer Woche'],
+        M: ['ein Monat', 'einem Monat'],
+        MM: [number + ' Monate', number + ' Monaten'],
+        y: ['ein Jahr', 'einem Jahr'],
+        yy: [number + ' Jahre', number + ' Jahren'],
+    };
+    return withoutSuffix ? format[key][0] : format[key][1];
+}
+
+export default moment.defineLocale('de', {
+    months: 'Januar_Februar_März_April_Mai_Juni_Juli_August_September_Oktober_November_Dezember'.split(
+        '_'
+    ),
+    monthsShort:
+        'Jan._Feb._März_Apr._Mai_Juni_Juli_Aug._Sep._Okt._Nov._Dez.'.split('_'),
+    monthsParseExact: true,
+    weekdays:
+        'Sonntag_Montag_Dienstag_Mittwoch_Donnerstag_Freitag_Samstag'.split(
+            '_'
+        ),
+    weekdaysShort: 'So._Mo._Di._Mi._Do._Fr._Sa.'.split('_'),
+    weekdaysMin: 'So_Mo_Di_Mi_Do_Fr_Sa'.split('_'),
+    weekdaysParseExact: true,
+    longDateFormat: {
+        LT: 'HH:mm',
+        LTS: 'HH:mm:ss',
+        L: 'DD.MM.YYYY',
+        LL: 'D. MMMM YYYY',
+        LLL: 'D. MMMM YYYY HH:mm',
+        LLLL: 'dddd, D. MMMM YYYY HH:mm',
+    },
+    calendar: {
+        sameDay: '[heute um] LT [Uhr]',
+        sameElse: 'L',
+        nextDay: '[morgen um] LT [Uhr]',
+        nextWeek: 'dddd [um] LT [Uhr]',
+        lastDay: '[gestern um] LT [Uhr]',
+        lastWeek: '[letzten] dddd [um] LT [Uhr]',
+    },
+    relativeTime: {
+        future: 'in %s',
+        past: 'vor %s',
+        s: 'ein paar Sekunden',
+        ss: '%d Sekunden',
+        m: processRelativeTime,
+        mm: '%d Minuten',
+        h: processRelativeTime,
+        hh: '%d Stunden',
+        d: processRelativeTime,
+        dd: processRelativeTime,
+        w: processRelativeTime,
+        ww: '%d Wochen',
+        M: processRelativeTime,
+        MM: processRelativeTime,
+        y: processRelativeTime,
+        yy: processRelativeTime,
+    },
+    dayOfMonthOrdinalParse: /\d{1,2}\./,
+    ordinal: '%d.',
+    week: {
+        dow: 1, // Monday is the first day of the week.
+        doy: 4, // The week that contains Jan 4th is the first week of the year.
+    },
+});

+ 90 - 0
node_modules/moment/dist/locale/dv.js

@@ -0,0 +1,90 @@
+//! moment.js locale configuration
+//! locale : Maldivian [dv]
+//! author : Jawish Hameed : https://github.com/jawish
+
+import moment from '../moment';
+
+var months = [
+        'ޖެނުއަރީ',
+        'ފެބްރުއަރީ',
+        'މާރިޗު',
+        'އޭޕްރީލު',
+        'މޭ',
+        'ޖޫން',
+        'ޖުލައި',
+        'އޯގަސްޓު',
+        'ސެޕްޓެމްބަރު',
+        'އޮކްޓޯބަރު',
+        'ނޮވެމްބަރު',
+        'ޑިސެމްބަރު',
+    ],
+    weekdays = [
+        'އާދިއްތަ',
+        'ހޯމަ',
+        'އަންގާރަ',
+        'ބުދަ',
+        'ބުރާސްފަތި',
+        'ހުކުރު',
+        'ހޮނިހިރު',
+    ];
+
+export default moment.defineLocale('dv', {
+    months: months,
+    monthsShort: months,
+    weekdays: weekdays,
+    weekdaysShort: weekdays,
+    weekdaysMin: 'އާދި_ހޯމަ_އަން_ބުދަ_ބުރާ_ހުކު_ހޮނި'.split('_'),
+    longDateFormat: {
+        LT: 'HH:mm',
+        LTS: 'HH:mm:ss',
+        L: 'D/M/YYYY',
+        LL: 'D MMMM YYYY',
+        LLL: 'D MMMM YYYY HH:mm',
+        LLLL: 'dddd D MMMM YYYY HH:mm',
+    },
+    meridiemParse: /މކ|މފ/,
+    isPM: function (input) {
+        return 'މފ' === input;
+    },
+    meridiem: function (hour, minute, isLower) {
+        if (hour < 12) {
+            return 'މކ';
+        } else {
+            return 'މފ';
+        }
+    },
+    calendar: {
+        sameDay: '[މިއަދު] LT',
+        nextDay: '[މާދަމާ] LT',
+        nextWeek: 'dddd LT',
+        lastDay: '[އިއްޔެ] LT',
+        lastWeek: '[ފާއިތުވި] dddd LT',
+        sameElse: 'L',
+    },
+    relativeTime: {
+        future: 'ތެރޭގައި %s',
+        past: 'ކުރިން %s',
+        s: 'ސިކުންތުކޮޅެއް',
+        ss: 'd% ސިކުންތު',
+        m: 'މިނިޓެއް',
+        mm: 'މިނިޓު %d',
+        h: 'ގަޑިއިރެއް',
+        hh: 'ގަޑިއިރު %d',
+        d: 'ދުވަހެއް',
+        dd: 'ދުވަސް %d',
+        M: 'މަހެއް',
+        MM: 'މަސް %d',
+        y: 'އަހަރެއް',
+        yy: 'އަހަރު %d',
+    },
+    preparse: function (string) {
+        return string.replace(/،/g, ',');
+    },
+    postformat: function (string) {
+        return string.replace(/,/g, '،');
+    },
+    week: {
+        dow: 7, // Sunday is the first day of the week.
+        doy: 12, // The week that contains Jan 12th is the first week of the year.
+    },
+});

+ 106 - 0
node_modules/moment/dist/locale/el.js

@@ -0,0 +1,106 @@
+//! moment.js locale configuration
+//! locale : Greek [el]
+//! author : Aggelos Karalias : https://github.com/mehiel
+
+import moment from '../moment';
+
+function isFunction(input) {
+    return (
+        (typeof Function !== 'undefined' && input instanceof Function) ||
+        Object.prototype.toString.call(input) === '[object Function]'
+    );
+}
+
+export default moment.defineLocale('el', {
+    monthsNominativeEl:
+        'Ιανουάριος_Φεβρουάριος_Μάρτιος_Απρίλιος_Μάιος_Ιούνιος_Ιούλιος_Αύγουστος_Σεπτέμβριος_Οκτώβριος_Νοέμβριος_Δεκέμβριος'.split(
+            '_'
+        ),
+    monthsGenitiveEl:
+        'Ιανουαρίου_Φεβρουαρίου_Μαρτίου_Απριλίου_Μαΐου_Ιουνίου_Ιουλίου_Αυγούστου_Σεπτεμβρίου_Οκτωβρίου_Νοεμβρίου_Δεκεμβρίου'.split(
+            '_'
+        ),
+    months: function (momentToFormat, format) {
+        if (!momentToFormat) {
+            return this._monthsNominativeEl;
+        } else if (
+            typeof format === 'string' &&
+            /D/.test(format.substring(0, format.indexOf('MMMM')))
+        ) {
+            // if there is a day number before 'MMMM'
+            return this._monthsGenitiveEl[momentToFormat.month()];
+        } else {
+            return this._monthsNominativeEl[momentToFormat.month()];
+        }
+    },
+    monthsShort: 'Ιαν_Φεβ_Μαρ_Απρ_Μαϊ_Ιουν_Ιουλ_Αυγ_Σεπ_Οκτ_Νοε_Δεκ'.split('_'),
+    weekdays: 'Κυριακή_Δευτέρα_Τρίτη_Τετάρτη_Πέμπτη_Παρασκευή_Σάββατο'.split(
+        '_'
+    ),
+    weekdaysShort: 'Κυρ_Δευ_Τρι_Τετ_Πεμ_Παρ_Σαβ'.split('_'),
+    weekdaysMin: 'Κυ_Δε_Τρ_Τε_Πε_Πα_Σα'.split('_'),
+    meridiem: function (hours, minutes, isLower) {
+        if (hours > 11) {
+            return isLower ? 'μμ' : 'ΜΜ';
+        } else {
+            return isLower ? 'πμ' : 'ΠΜ';
+        }
+    },
+    isPM: function (input) {
+        return (input + '').toLowerCase()[0] === 'μ';
+    },
+    meridiemParse: /[ΠΜ]\.?Μ?\.?/i,
+    longDateFormat: {
+        LT: 'h:mm A',
+        LTS: 'h:mm:ss A',
+        L: 'DD/MM/YYYY',
+        LL: 'D MMMM YYYY',
+        LLL: 'D MMMM YYYY h:mm A',
+        LLLL: 'dddd, D MMMM YYYY h:mm A',
+    },
+    calendarEl: {
+        sameDay: '[Σήμερα {}] LT',
+        nextDay: '[Αύριο {}] LT',
+        nextWeek: 'dddd [{}] LT',
+        lastDay: '[Χθες {}] LT',
+        lastWeek: function () {
+            switch (this.day()) {
+                case 6:
+                    return '[το προηγούμενο] dddd [{}] LT';
+                default:
+                    return '[την προηγούμενη] dddd [{}] LT';
+            }
+        },
+        sameElse: 'L',
+    },
+    calendar: function (key, mom) {
+        var output = this._calendarEl[key],
+            hours = mom && mom.hours();
+        if (isFunction(output)) {
+            output = output.apply(mom);
+        }
+        return output.replace('{}', hours % 12 === 1 ? 'στη' : 'στις');
+    },
+    relativeTime: {
+        future: 'σε %s',
+        past: '%s πριν',
+        s: 'λίγα δευτερόλεπτα',
+        ss: '%d δευτερόλεπτα',
+        m: 'ένα λεπτό',
+        mm: '%d λεπτά',
+        h: 'μία ώρα',
+        hh: '%d ώρες',
+        d: 'μία μέρα',
+        dd: '%d μέρες',
+        M: 'ένας μήνας',
+        MM: '%d μήνες',
+        y: 'ένας χρόνος',
+        yy: '%d χρόνια',
+    },
+    dayOfMonthOrdinalParse: /\d{1,2}η/,
+    ordinal: '%dη',
+    week: {
+        dow: 1, // Monday is the first day of the week.
+        doy: 4, // The week that contains Jan 4st is the first week of the year.
+    },
+});

Неке датотеке нису приказане због велике количине промена