Просмотр исходного кода

Merge branch 'dev' of ssh://10.10.100.21/source/saas-platform into dev

rainco 7 лет назад
Родитель
Сommit
cb56a31342
100 измененных файлов с 7573 добавлено и 112 удалено
  1. 2 0
      .gitignore
  2. 1 0
      applications/commons/commons-dto/src/main/java/com/usoftchina/saas/commons/exception/BizExceptionCode.java
  3. 7 0
      applications/document/document-server/src/main/java/com/usoftchina/saas/document/controller/BankinformationController.java
  4. 3 0
      applications/document/document-server/src/main/java/com/usoftchina/saas/document/mapper/BankinformationMapper.java
  5. 2 1
      applications/document/document-server/src/main/java/com/usoftchina/saas/document/service/BankinformationService.java
  6. 6 0
      applications/document/document-server/src/main/java/com/usoftchina/saas/document/service/impl/BankinformationServiceImpl.java
  7. 3 0
      applications/document/document-server/src/main/resources/mapper/BankinformationMapper.xml
  8. 2 1
      applications/document/document-server/src/main/resources/mapper/ProductMapper.xml
  9. 1 1
      applications/money/money-server/src/main/java/com/usoftchina/saas/money/controller/SubledgerController.java
  10. 1 0
      applications/money/money-server/src/main/java/com/usoftchina/saas/money/mapper/BanksubledgerMapper.java
  11. 1 0
      applications/money/money-server/src/main/java/com/usoftchina/saas/money/mapper/SubledgerMapper.java
  12. 44 0
      applications/money/money-server/src/main/java/com/usoftchina/saas/money/service/impl/FundtransferServiceImpl.java
  13. 46 0
      applications/money/money-server/src/main/java/com/usoftchina/saas/money/service/impl/OthreceiptsServiceImpl.java
  14. 46 4
      applications/money/money-server/src/main/java/com/usoftchina/saas/money/service/impl/OthspendingsServiceImpl.java
  15. 20 5
      applications/money/money-server/src/main/java/com/usoftchina/saas/money/service/impl/PaybalanceServiceImpl.java
  16. 100 3
      applications/money/money-server/src/main/java/com/usoftchina/saas/money/service/impl/RecbalanceServiceImpl.java
  17. 8 4
      applications/money/money-server/src/main/resources/mapper/BanksubledgerMapper.xml
  18. 7 1
      applications/money/money-server/src/main/resources/mapper/SubledgerMapper.xml
  19. 3 0
      applications/purchase/purchase-server/src/main/java/com/usoftchina/saas/purchase/service/impl/PurchaseServiceImpl.java
  20. 8 8
      applications/purchase/purchase-server/src/main/resources/mapper/ProdInOutMapper.xml
  21. 1 1
      applications/sale/sale-dto/src/main/java/com/usoftchina/saas/sale/dto/ProdInOutDTO.java
  22. 1 0
      applications/sale/sale-dto/src/main/java/com/usoftchina/saas/sale/dto/ProdInOutListDTO.java
  23. 2 0
      applications/sale/sale-server/src/main/java/com/usoftchina/saas/sale/mapper/SaleMapper.java
  24. 6 4
      applications/sale/sale-server/src/main/java/com/usoftchina/saas/sale/service/impl/ProdInOutServiceImpl.java
  25. 5 3
      applications/sale/sale-server/src/main/java/com/usoftchina/saas/sale/service/impl/SaleServiceImpl.java
  26. 6 5
      applications/sale/sale-server/src/main/resources/mapper/ProdInOutListMapper.xml
  27. 19 7
      applications/sale/sale-server/src/main/resources/mapper/ProdInOutMapper.xml
  28. 5 0
      applications/sale/sale-server/src/main/resources/mapper/SaleMapper.xml
  29. 8 0
      applications/storage/storage-dto/src/main/java/com/usoftchina/saas/storage/dto/MakeListDTO.java
  30. 1 1
      applications/storage/storage-dto/src/main/java/com/usoftchina/saas/storage/dto/ProdInOutDTO.java
  31. 1 0
      applications/storage/storage-dto/src/main/java/com/usoftchina/saas/storage/dto/ProdInOutListDTO.java
  32. 13 3
      applications/storage/storage-dto/src/main/java/com/usoftchina/saas/storage/po/Make.java
  33. 3 3
      applications/storage/storage-dto/src/main/java/com/usoftchina/saas/storage/po/MakeMaterial.java
  34. 1 1
      applications/storage/storage-dto/src/main/java/com/usoftchina/saas/storage/po/ProdInOut.java
  35. 1 0
      applications/storage/storage-dto/src/main/java/com/usoftchina/saas/storage/po/ProdInOutList.java
  36. 29 0
      applications/storage/storage-server/src/main/java/com/usoftchina/saas/storage/controller/MakeController.java
  37. 2 0
      applications/storage/storage-server/src/main/java/com/usoftchina/saas/storage/mapper/MakeMapper.java
  38. 36 0
      applications/storage/storage-server/src/main/java/com/usoftchina/saas/storage/service/MakeService.java
  39. 355 6
      applications/storage/storage-server/src/main/java/com/usoftchina/saas/storage/service/impl/MakeServiceImpl.java
  40. 18 3
      applications/storage/storage-server/src/main/resources/mapper/MakeMapper.xml
  41. 21 7
      applications/storage/storage-server/src/main/resources/mapper/MakematerialMapper.xml
  42. 8 8
      applications/storage/storage-server/src/main/resources/mapper/ProdInOutMapper.xml
  43. 10 0
      base-servers/account/account-api/src/main/java/com/usoftchina/saas/account/cache/AccountCache.java
  44. 10 0
      base-servers/account/account-api/src/main/java/com/usoftchina/saas/account/cache/ResourceCache.java
  45. 18 0
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/controller/AccountController.java
  46. 1 1
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/controller/ResourceController.java
  47. 16 0
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/mapper/AccountMapper.java
  48. 8 0
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/service/AccountService.java
  49. 14 0
      base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/service/impl/AccountServiceImpl.java
  50. 3 0
      base-servers/account/account-server/src/main/resources/application.yml
  51. 58 0
      base-servers/account/account-server/src/main/resources/mapper/AccountMapper.xml
  52. 9 0
      base-servers/auth/auth-server/pom.xml
  53. 2 0
      base-servers/auth/auth-server/src/main/java/com/usoftchina/saas/auth/AuthApplication.java
  54. 44 1
      base-servers/auth/auth-server/src/main/java/com/usoftchina/saas/auth/controller/AuthController.java
  55. 47 0
      base-servers/auth/auth-server/src/main/java/com/usoftchina/saas/auth/dto/AuthorizeLogDTO.java
  56. 29 0
      base-servers/auth/auth-server/src/main/java/com/usoftchina/saas/auth/mapper/AuthorizeLogMapper.java
  57. 117 0
      base-servers/auth/auth-server/src/main/java/com/usoftchina/saas/auth/po/AuthorizeLog.java
  58. 68 0
      base-servers/auth/auth-server/src/main/java/com/usoftchina/saas/auth/service/AuthorizeCountService.java
  59. 28 0
      base-servers/auth/auth-server/src/main/java/com/usoftchina/saas/auth/service/AuthorizeLogService.java
  60. 42 0
      base-servers/auth/auth-server/src/main/java/com/usoftchina/saas/auth/service/impl/AuthorizeLogServiceImpl.java
  61. 3 0
      base-servers/auth/auth-server/src/main/resources/application.yml
  62. 21 0
      base-servers/auth/auth-server/src/main/resources/mapper/AuthorizeLogMapper.xml
  63. 48 0
      base-servers/auth/auth-server/src/test/java/com/usoftchina/saas/auth/controller/AuthControllerTest.java
  64. 2 2
      base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/AuthFilter.java
  65. 3 0
      base-servers/gateway-server/src/main/resources/application.yml
  66. 3 0
      base-servers/ui-server/src/main/resources/application.yml
  67. 6 0
      framework/core/pom.xml
  68. 15 2
      framework/core/src/main/java/com/usoftchina/saas/base/Result.java
  69. 2 11
      framework/core/src/main/java/com/usoftchina/saas/cache/BaseRedisCache.java
  70. 40 0
      framework/core/src/main/java/com/usoftchina/saas/cache/CacheKeyHelper.java
  71. 5 0
      framework/core/src/main/java/com/usoftchina/saas/exception/ExceptionCode.java
  72. 23 0
      framework/core/src/main/java/com/usoftchina/saas/page/PageDefault.java
  73. 12 0
      framework/core/src/main/java/com/usoftchina/saas/page/PageRequest.java
  74. 96 0
      framework/core/src/main/java/com/usoftchina/saas/page/PageRequestArgumentResolver.java
  75. 13 13
      framework/core/src/main/java/com/usoftchina/saas/utils/JsonUtils.java
  76. 29 0
      framework/server-starter/src/main/java/com/usoftchina/saas/server/web/DefaultWebMvcConfig.java
  77. 17 2
      framework/test-starter/src/main/java/com.usoftchina.saas.test/BaseControllerTest.java
  78. 15 0
      frontend/saas-portal-web/.gitignore
  79. 5 0
      frontend/saas-portal-web/Dockerfile
  80. 15 0
      frontend/saas-portal-web/README.md
  81. 5 0
      frontend/saas-portal-web/config/dev.env.js
  82. 5 0
      frontend/saas-portal-web/config/prod.env.js
  83. 21 0
      frontend/saas-portal-web/package.json
  84. 20 0
      frontend/saas-portal-web/runtime/nginx/default.conf
  85. 3296 0
      frontend/saas-portal-web/src/css/animate.css
  86. 4 0
      frontend/saas-portal-web/src/css/bootstrap.min.css
  87. 13 0
      frontend/saas-portal-web/src/css/ionicons.min.css
  88. 274 0
      frontend/saas-portal-web/src/css/jquery.fancybox.css
  89. 1733 0
      frontend/saas-portal-web/src/css/main.css
  90. 231 0
      frontend/saas-portal-web/src/css/owl.carousel.css
  91. 221 0
      frontend/saas-portal-web/src/css/slit-slider.css
  92. BIN
      frontend/saas-portal-web/src/fonts/ionicons.ttf
  93. BIN
      frontend/saas-portal-web/src/fonts/ionicons.woff
  94. BIN
      frontend/saas-portal-web/src/img/features.jpg
  95. BIN
      frontend/saas-portal-web/src/img/icons/quotes.png
  96. BIN
      frontend/saas-portal-web/src/img/logo.png
  97. BIN
      frontend/saas-portal-web/src/img/member-1.jpg
  98. BIN
      frontend/saas-portal-web/src/img/parallax/testimonial.jpg
  99. BIN
      frontend/saas-portal-web/src/img/portfolio/item-1.jpg
  100. BIN
      frontend/saas-portal-web/src/img/portfolio/item-2.jpg

+ 2 - 0
.gitignore

@@ -29,8 +29,10 @@ applications/**/build/
 base-servers/**/build/
 framework/**/build/
 frontend/saas-web/build/
+frontend/saas-portal-web/build/
 nbbuild/
 dist/
+node_modules/
 nbdist/
 .nb-gradle/
 generatorConfig.xml

+ 1 - 0
applications/commons/commons-dto/src/main/java/com/usoftchina/saas/commons/exception/BizExceptionCode.java

@@ -44,6 +44,7 @@ public enum BizExceptionCode implements BaseExceptionCode {
     SALEOUT_ALL_TURNIN(72003, "该出货单已全部转退货,无法转销售退货单"),
     SALEOUT_POSTSTATUS_ERROR(72004,"当前单据状态无法进行此操作。"),
     SALEOUT_POST_ERROR(72005,""),
+    SALEOUT_UNAUDIT_ERROR(72006,"销售订单已转出货单,无法反审核"),
     //资金
 
     //库存

+ 7 - 0
applications/document/document-server/src/main/java/com/usoftchina/saas/document/controller/BankinformationController.java

@@ -2,6 +2,7 @@ package com.usoftchina.saas.document.controller;
 
 import com.github.pagehelper.PageInfo;
 import com.usoftchina.saas.base.Result;
+import com.usoftchina.saas.commons.dto.ComboDTO;
 import com.usoftchina.saas.commons.dto.ListReqDTO;
 import com.usoftchina.saas.document.entities.Bankinformation;
 import com.usoftchina.saas.document.service.BankinformationService;
@@ -38,4 +39,10 @@ public class BankinformationController {
         PageInfo<Bankinformation> bankinformationList = bankinformationService.selectList(page, condition);
         return Result.success(bankinformationList);
     }
+
+    @GetMapping("/getCombo")
+    public Result getCombo(){
+        List<ComboDTO> comboDTOList = bankinformationService.getCombo();
+        return Result.success(comboDTOList);
+    }
 }

+ 3 - 0
applications/document/document-server/src/main/java/com/usoftchina/saas/document/mapper/BankinformationMapper.java

@@ -1,6 +1,7 @@
 package com.usoftchina.saas.document.mapper;
 
 import com.usoftchina.saas.base.mapper.CommonBaseMapper;
+import com.usoftchina.saas.commons.dto.ComboDTO;
 import com.usoftchina.saas.document.entities.Bankinformation;
 import org.apache.ibatis.annotations.Param;
 
@@ -22,4 +23,6 @@ public interface BankinformationMapper extends CommonBaseMapper<Bankinformation>
     int updateByPrimaryKey(Bankinformation record);
 
     List<Bankinformation> selectBankinformationBycondition(@Param("con") String con, @Param("companyId") Long companyId);
+
+    List<ComboDTO> getCombo(@Param("companyId") Long companyId);
 }

+ 2 - 1
applications/document/document-server/src/main/java/com/usoftchina/saas/document/service/BankinformationService.java

@@ -2,6 +2,7 @@ package com.usoftchina.saas.document.service;
 
 import com.github.pagehelper.PageInfo;
 import com.usoftchina.saas.base.service.CommonBaseService;
+import com.usoftchina.saas.commons.dto.ComboDTO;
 import com.usoftchina.saas.commons.dto.ListReqDTO;
 import com.usoftchina.saas.document.entities.Bankinformation;
 import com.usoftchina.saas.document.mapper.BankinformationMapper;
@@ -21,5 +22,5 @@ public interface BankinformationService extends CommonBaseService<Bankinformatio
 
     PageInfo<Bankinformation> selectList(PageRequest page, ListReqDTO condition);
 
-
+    List<ComboDTO> getCombo();
 }

+ 6 - 0
applications/document/document-server/src/main/java/com/usoftchina/saas/document/service/impl/BankinformationServiceImpl.java

@@ -3,6 +3,7 @@ package com.usoftchina.saas.document.service.impl;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import com.usoftchina.saas.base.service.CommonBaseServiceImpl;
+import com.usoftchina.saas.commons.dto.ComboDTO;
 import com.usoftchina.saas.commons.dto.ListReqDTO;
 import com.usoftchina.saas.context.BaseContextHolder;
 import com.usoftchina.saas.document.entities.Bankinformation;
@@ -69,4 +70,9 @@ public class BankinformationServiceImpl extends CommonBaseServiceImpl<Bankinform
         recbalancesList = bankinformationMapper.selectBankinformationBycondition(con, companyId);
         return recbalancesList;
     }
+
+    @Override
+    public List<ComboDTO> getCombo() {
+        return getMapper().getCombo(BaseContextHolder.getCompanyId());
+    }
 }

+ 3 - 0
applications/document/document-server/src/main/resources/mapper/BankinformationMapper.xml

@@ -334,4 +334,7 @@
       </if>
     </where>  order by bk_id
   </select>
+  <select id="getCombo" resultType="com.usoftchina.saas.commons.dto.ComboDTO">
+        SELECT CONCAT(bk_bankcode,' ',bk_bankname) display,bk_bankname value FROM bankinformation WHERE COMPANYID=#{companyId}
+    </select>
 </mapper>

+ 2 - 1
applications/document/document-server/src/main/resources/mapper/ProductMapper.xml

@@ -61,12 +61,13 @@
     <!--查询所有物料信息-->
     <select id="getProductsByCondition" resultMap="ProductDTOResultMapper" parameterType="com.usoftchina.saas.commons.dto.DocReqDTO">
         SELECT * FROM PRODUCT left join productonhand on pr_id = po_prodid
+        and  PRODUCT.companyid =  productonhand.companyid
         <where>
             <if test="con != null">
                 ${con}
             </if>
             <if test="companyId != null">
-                and  PRODUCT.companyid = #{companyId} and productonhand.companyId=#{companyId}
+                and  PRODUCT.companyid = #{companyId}
             </if>
         </where>
     </select>

+ 1 - 1
applications/money/money-server/src/main/java/com/usoftchina/saas/money/controller/SubledgerController.java

@@ -20,7 +20,7 @@ public class SubledgerController {
     @Autowired
     private SubledgerService subledgerService;
 
-    @GetMapping("/list")
+    @RequestMapping("/list")
     public Result getListData(PageRequest page, ListReqDTO condition){
         return Result.success(subledgerService.seleteList(page, condition));
     }

+ 1 - 0
applications/money/money-server/src/main/java/com/usoftchina/saas/money/mapper/BanksubledgerMapper.java

@@ -22,6 +22,7 @@ public interface BanksubledgerMapper extends CommonBaseMapper<Banksubledger> {
 //    Long insert(Banksubledger record);
 
     int insertSelective(Banksubledger record);
+    String selectCode(String code);
 
     List<Banksubledger> selectByExample(BanksubledgerExample example);
 

+ 1 - 0
applications/money/money-server/src/main/java/com/usoftchina/saas/money/mapper/SubledgerMapper.java

@@ -19,6 +19,7 @@ public interface SubledgerMapper {
     int insertSelective(Subledger record);
 
     Subledger selectByPrimaryKey(Integer sl_id);
+    String selectCode(String code);
 
     int updateByPrimaryKeySelective(Subledger record);
 

+ 44 - 0
applications/money/money-server/src/main/java/com/usoftchina/saas/money/service/impl/FundtransferServiceImpl.java

@@ -11,6 +11,7 @@ import com.usoftchina.saas.commons.po.BillCodeSeq;
 import com.usoftchina.saas.commons.po.Status;
 import com.usoftchina.saas.context.BaseContextHolder;
 import com.usoftchina.saas.exception.BizException;
+import com.usoftchina.saas.money.mapper.BanksubledgerMapper;
 import com.usoftchina.saas.money.mapper.FundtransferMapper;
 import com.usoftchina.saas.money.mapper.FundtransferdetailMapper;
 import com.usoftchina.saas.money.po.*;
@@ -35,6 +36,8 @@ public class FundtransferServiceImpl implements FundtransferService {
     private FundtransferdetailMapper fundtransferdetailMapper;
     @Autowired
     private MaxnumberService maxnumberService;
+    @Autowired
+    private BanksubledgerMapper banksubledgerMapper;
 
     @Override
     public DocBaseDTO insert(Fundtran fundtran) {
@@ -84,6 +87,7 @@ public class FundtransferServiceImpl implements FundtransferService {
         fundtransfer.setFt_status(Status.AUDITED.getDisplay());
         fundtransfer.setFt_statuscode(Status.AUDITED.name());
         fundtransfer.setCompanyId(companyId);
+        this.changBankUntil(fundtran);
         if ( fundtransfer == null || "".equals(fundtransfer)){
             this.insert(fundtran);
         }else {
@@ -98,6 +102,10 @@ public class FundtransferServiceImpl implements FundtransferService {
         fundtransfer.setFt_status(com.usoftchina.saas.commons.po.Status.UNAUDITED.getDisplay());
         fundtransfer.setFt_statuscode(Status.UNAUDITED.name());
         fundtransferMapper.updateByPrimaryKeySelective(fundtransfer);
+
+        //删除中间表
+        fundtransfer = fundtransferMapper.selectByPrimaryKey(id);
+        banksubledgerMapper.deleteByPrimaryKey(fundtransfer.getFt_code(), "其他收入单");
     }
 
 
@@ -130,6 +138,8 @@ public class FundtransferServiceImpl implements FundtransferService {
             fundtransfer.setId(Long.valueOf(docBaseDTO.getId()));
             fundtransfer.setFt_status(com.usoftchina.saas.commons.po.Status.AUDITED.getDisplay());
             fundtransfer.setFt_statuscode(Status.AUDITED.name());
+            Fundtran fundtran = this.select(Math.toIntExact(fundtransfer.getId()));
+            this.changBankUntil(fundtran);
             fundtransferMapper.updateByPrimaryKeySelective(fundtransfer);
         }
     }
@@ -200,4 +210,38 @@ public class FundtransferServiceImpl implements FundtransferService {
         }
         return fundtransferList;
     }
+
+    //插入bank中间表
+    public void changBankUntil(Fundtran fundtran){
+        Banksubledger banksubledger = new Banksubledger();
+        Fundtransfer fundtransfer = fundtran.getMain();
+        List<Fundtransferdetail> fundtransferdetailsList = fundtran.getItems();
+        Iterator isList = fundtransferdetailsList.iterator();
+        while (isList.hasNext()) {
+            Fundtransferdetail fundtransferdetail = (Fundtransferdetail) isList.next();
+            banksubledger.setCompanyId(BaseContextHolder.getCompanyId());
+            banksubledger.setBl_ym(fundtransferdetail.getFtd_ym());
+            banksubledger.setBl_bankid(fundtransferdetail.getFtd_bankid());
+            banksubledger.setBl_bankcode(fundtransferdetail.getFtd_bankcode());
+            banksubledger.setBl_bankname(fundtransferdetail.getFtd_bankname());
+            banksubledger.setBl_code(fundtransfer.getFt_code());
+            banksubledger.setBl_kind("资金转账");
+            banksubledger.setBl_date(fundtransfer.getFt_date());
+
+//            if (fundtransferdetail.getOs_vendid() == null || fundtransferdetail.getOs_vendid() != 0){
+//                banksubledger.setBl_asstype("供应商往来 ");
+//            }else {
+//                banksubledger.setBl_asstype(null);
+//            }
+            banksubledger.setBl_assid(0);
+            banksubledger.setBl_spending(fundtransferdetail.getFtd_nowbalance());
+            banksubledger.setBl_remark(fundtransferdetail.getFtd_remark());
+            banksubledger.setBl_orderamount(fundtransferdetail.getFtd_nowbalance() * -1);
+            if (banksubledgerMapper.selectCode(banksubledger.getBl_code()) == null){
+                banksubledgerMapper.insertSelective(banksubledger);
+            }else {
+                banksubledgerMapper.updateByPrimaryKeySelective(banksubledger);
+            }
+        }
+    }
 }

+ 46 - 0
applications/money/money-server/src/main/java/com/usoftchina/saas/money/service/impl/OthreceiptsServiceImpl.java

@@ -11,6 +11,7 @@ import com.usoftchina.saas.commons.po.BillCodeSeq;
 import com.usoftchina.saas.commons.po.Status;
 import com.usoftchina.saas.context.BaseContextHolder;
 import com.usoftchina.saas.exception.BizException;
+import com.usoftchina.saas.money.mapper.BanksubledgerMapper;
 import com.usoftchina.saas.money.mapper.OthreceiptsMapper;
 import com.usoftchina.saas.money.mapper.OthreceiptsdetailMapper;
 import com.usoftchina.saas.money.po.*;
@@ -36,6 +37,8 @@ public class OthreceiptsServiceImpl implements OthreceiptsService {
     private OthreceiptsdetailMapper othreceiptsdetailMapper;
     @Autowired
     private MaxnumberService maxnumberService;
+    @Autowired
+    private BanksubledgerMapper banksubledgerMapper;
 
     @Override
     public DocBaseDTO insert(Othte othte) {
@@ -85,6 +88,7 @@ public class OthreceiptsServiceImpl implements OthreceiptsService {
         othreceipts.setOr_status(Status.AUDITED.getDisplay());
         othreceipts.setOr_statuscode(Status.AUDITED.name());
         othreceipts.setCompanyId(companyId);
+        this.changBankUntil(othte);
         if ( othreceipts == null || "".equals(othreceipts)){
             this.insert(othte);
         }else {
@@ -99,6 +103,10 @@ public class OthreceiptsServiceImpl implements OthreceiptsService {
         othreceipts.setOr_status(com.usoftchina.saas.commons.po.Status.UNAUDITED.getDisplay());
         othreceipts.setOr_statuscode(Status.UNAUDITED.name());
         othreceiptsMapper.updateByPrimaryKeySelective(othreceipts);
+
+        //删除中间表
+        othreceipts = othreceiptsMapper.selectByPrimaryKey(id);
+        banksubledgerMapper.deleteByPrimaryKey(othreceipts.getOr_code(), "其他收入单");
     }
 
 
@@ -153,6 +161,8 @@ public class OthreceiptsServiceImpl implements OthreceiptsService {
             othreceipts.setId(Long.valueOf(docBaseDTO.getId()));
             othreceipts.setOr_status(com.usoftchina.saas.commons.po.Status.AUDITED.getDisplay());
             othreceipts.setOr_statuscode(Status.AUDITED.name());
+            Othte othte = this.select(Math.toIntExact(othreceipts.getId()));
+            this.changBankUntil(othte);
             othreceiptsMapper.updateByPrimaryKeySelective(othreceipts);
         }
     }
@@ -200,4 +210,40 @@ public class OthreceiptsServiceImpl implements OthreceiptsService {
         }
         return othreceiptsList;
     }
+
+    //插入bank中间表
+    public void changBankUntil(Othte othte){
+        Banksubledger banksubledger = new Banksubledger();
+        Othreceipts othreceipts = othte.getMain();
+        List<Othreceiptsdetail> othreceiptsdetailList = othte.getItems();
+        Iterator isList = othreceiptsdetailList.iterator();
+        while (isList.hasNext()) {
+            Othreceiptsdetail othreceiptsdetail = (Othreceiptsdetail) isList.next();
+            banksubledger.setCompanyId(BaseContextHolder.getCompanyId());
+            banksubledger.setBl_ym(othreceiptsdetail.getOrd_ym());
+            banksubledger.setBl_bankid(othreceipts.getOr_bankid());
+            banksubledger.setBl_bankcode(othreceipts.getOr_bankcode());
+            banksubledger.setBl_bankname(othreceipts.getOr_bankname());
+            banksubledger.setBl_code(othreceipts.getOr_code());
+            banksubledger.setBl_kind("其他收入单");
+            banksubledger.setBl_date(othreceipts.getOr_date());
+
+            if (othreceipts.getOr_custid() == null || othreceipts.getOr_custid() != 0){
+                banksubledger.setBl_asstype("客户往来");
+            }else {
+                banksubledger.setBl_asstype(null);
+            }
+            banksubledger.setBl_assid(othreceipts.getOr_custid());
+            banksubledger.setBl_asscode(othreceipts.getOr_custcode());
+            banksubledger.setBl_assname(othreceipts.getOr_custname());
+            banksubledger.setBl_income(othreceiptsdetail.getOrd_nowbalance());
+            banksubledger.setBl_remark(othreceiptsdetail.getOrd_remark());
+            banksubledger.setBl_orderamount(othreceiptsdetail.getOrd_nowbalance());
+            if (banksubledgerMapper.selectCode(banksubledger.getBl_code()) == null){
+                banksubledgerMapper.insertSelective(banksubledger);
+            }else {
+                banksubledgerMapper.updateByPrimaryKeySelective(banksubledger);
+            }
+        }
+    }
 }

+ 46 - 4
applications/money/money-server/src/main/java/com/usoftchina/saas/money/service/impl/OthspendingsServiceImpl.java

@@ -11,10 +11,7 @@ import com.usoftchina.saas.commons.po.BillCodeSeq;
 import com.usoftchina.saas.commons.po.Status;
 import com.usoftchina.saas.context.BaseContextHolder;
 import com.usoftchina.saas.exception.BizException;
-import com.usoftchina.saas.money.mapper.OthreceiptsMapper;
-import com.usoftchina.saas.money.mapper.OthreceiptsdetailMapper;
-import com.usoftchina.saas.money.mapper.OthspendingsMapper;
-import com.usoftchina.saas.money.mapper.OthspendingsdetailMapper;
+import com.usoftchina.saas.money.mapper.*;
 import com.usoftchina.saas.money.po.*;
 import com.usoftchina.saas.money.service.OthspendingsService;
 import com.usoftchina.saas.page.PageRequest;
@@ -37,6 +34,8 @@ public class OthspendingsServiceImpl implements OthspendingsService {
     private OthspendingsdetailMapper othspendingsdetailMapper;
     @Autowired
     private MaxnumberService maxnumberService;
+    @Autowired
+    private BanksubledgerMapper banksubledgerMapper;
 
     @Override
     public DocBaseDTO insert(Othsp othsp) {
@@ -117,6 +116,7 @@ public class OthspendingsServiceImpl implements OthspendingsService {
         othspendings.setOs_status(Status.AUDITED.getDisplay());
         othspendings.setOs_statuscode(Status.AUDITED.name());
         othspendings.setCompanyId(companyId);
+        this.changBankUntil(othsp);
         if ( othspendings == null || "".equals(othspendings)){
             this.insert(othsp);
         }else {
@@ -131,6 +131,10 @@ public class OthspendingsServiceImpl implements OthspendingsService {
         othspendings.setOs_status(com.usoftchina.saas.commons.po.Status.UNAUDITED.getDisplay());
         othspendings.setOs_statuscode(Status.UNAUDITED.name());
         othspendingsMapper.updateByPrimaryKeySelective(othspendings);
+
+        //删除中间表
+        othspendings = othspendingsMapper.selectByPrimaryKey(id);
+        banksubledgerMapper.deleteByPrimaryKey(othspendings.getOs_code(), "其他收入单");
     }
 
     @Override
@@ -151,6 +155,8 @@ public class OthspendingsServiceImpl implements OthspendingsService {
             othspendings.setId(Long.valueOf(docBaseDTO.getId()));
             othspendings.setOs_status(com.usoftchina.saas.commons.po.Status.AUDITED.getDisplay());
             othspendings.setOs_statuscode(Status.AUDITED.name());
+            Othsp othsp = this.select(Math.toIntExact(othspendings.getId()));
+            this.changBankUntil(othsp);
             othspendingsMapper.updateByPrimaryKeySelective(othspendings);
         }
     }
@@ -198,4 +204,40 @@ public class OthspendingsServiceImpl implements OthspendingsService {
         }
         return othspendingsList;
     }
+
+    //插入bank中间表
+    public void changBankUntil(Othsp othsp){
+        Banksubledger banksubledger = new Banksubledger();
+        Othspendings othspendings = othsp.getMain();
+        List<Othspendingsdetail> othspendingsdetailList = othsp.getItems();
+        Iterator isList = othspendingsdetailList.iterator();
+        while (isList.hasNext()) {
+            Othspendingsdetail othspendingsdetail = (Othspendingsdetail) isList.next();
+            banksubledger.setCompanyId(BaseContextHolder.getCompanyId());
+            banksubledger.setBl_ym(othspendingsdetail.getOsd_ym());
+            banksubledger.setBl_bankid(othspendings.getOs_bankid());
+            banksubledger.setBl_bankcode(othspendings.getOs_bankcode());
+            banksubledger.setBl_bankname(othspendings.getOs_bankname());
+            banksubledger.setBl_code(othspendings.getOs_code());
+            banksubledger.setBl_kind("其他支出单");
+            banksubledger.setBl_date(othspendings.getOs_date());
+
+            if (othspendings.getOs_vendid() == null || othspendings.getOs_vendid() != 0){
+                banksubledger.setBl_asstype("供应商往来 ");
+            }else {
+                banksubledger.setBl_asstype(null);
+            }
+            banksubledger.setBl_assid(othspendings.getOs_vendid());
+            banksubledger.setBl_asscode(othspendings.getOs_vendcode());
+            banksubledger.setBl_assname(othspendings.getOs_vendname());
+            banksubledger.setBl_income(othspendingsdetail.getOsd_nowbalance());
+            banksubledger.setBl_remark(othspendingsdetail.getOsd_remark());
+            banksubledger.setBl_orderamount(othspendingsdetail.getOsd_nowbalance() * -1);
+            if (banksubledgerMapper.selectCode(banksubledger.getBl_code()) == null){
+                banksubledgerMapper.insertSelective(banksubledger);
+            }else {
+                banksubledgerMapper.updateByPrimaryKeySelective(banksubledger);
+            }
+        }
+    }
 }

+ 20 - 5
applications/money/money-server/src/main/java/com/usoftchina/saas/money/service/impl/PaybalanceServiceImpl.java

@@ -103,10 +103,13 @@ public class PaybalanceServiceImpl implements PaybalanceService {
         paybalance.setPb_statuscode(Status.AUDITED.name());
         paybalance.setCompanyId(companyId);
         Subledger subledger = this.changSubledgerUntil(paybalance);
-        subledgerMapper.insertSelective(subledger);
+        if (subledger.getSl_code() == null){
+            subledgerMapper.insertSelective(subledger);
+        }else {
+            subledgerMapper.updateByPrimaryKeySelective(subledger);
+        }
         this.changBankUntil(pay);
         System.out.println("新增");
-        this.changBankUntil(pay);
         if ( paybalance == null || "".equals(paybalance)){
             this.insert(pay);
         }else {
@@ -185,6 +188,14 @@ public class PaybalanceServiceImpl implements PaybalanceService {
             paybalance.setId(Long.valueOf(docBaseDTO.getId()));
             paybalance.setPb_status(com.usoftchina.saas.commons.po.Status.AUDITED.getDisplay());
             paybalance.setPb_statuscode(Status.AUDITED.name());
+            Pay pay = this.select(Math.toIntExact(paybalance.getId()));
+            Subledger subledger = this.changSubledgerUntil(pay.getMain());
+            if (subledger.getSl_code() == null){
+                subledgerMapper.insertSelective(subledger);
+            }else {
+                subledgerMapper.updateByPrimaryKeySelective(subledger);
+            }
+            this.changBankUntil(pay);
             paybalanceMapper.updateByPrimaryKeySelective(paybalance);
         }
     }
@@ -293,16 +304,20 @@ public class PaybalanceServiceImpl implements PaybalanceService {
             banksubledger.setBl_date(paybalance.getPb_date());
 
             if (paybalance.getPb_vendid() == null || paybalance.getPb_vendid() != 0){
-                banksubledger.setBl_asscode("供应商往来");
+                banksubledger.setBl_asstype("供应商往来");
             }
             banksubledger.setBl_assid(paybalance.getPb_vendid());
-            banksubledger.setBl_code(paybalance.getPb_code());
+            banksubledger.setBl_asscode(paybalance.getPb_vendcode());
             banksubledger.setBl_assname(paybalance.getPb_vendname());
             banksubledger.setBl_spending(paybalancedet.getPd_amount());
             banksubledger.setBl_manname(paybalance.getPb_manname());
             banksubledger.setBl_remark(paybalancedet.getPd_remark());
             banksubledger.setBl_orderamount(paybalancedet.getPd_amount() * -1);
-            banksubledgerMapper.insertSelective(banksubledger);
+            if (banksubledgerMapper.selectCode(banksubledger.getBl_code()) == null){
+                banksubledgerMapper.insertSelective(banksubledger);
+            }else {
+                banksubledgerMapper.updateByPrimaryKeySelective(banksubledger);
+            }
 
         }
     }

+ 100 - 3
applications/money/money-server/src/main/java/com/usoftchina/saas/money/service/impl/RecbalanceServiceImpl.java

@@ -11,9 +11,7 @@ import com.usoftchina.saas.commons.po.BillCodeSeq;
 import com.usoftchina.saas.commons.po.Status;
 import com.usoftchina.saas.context.BaseContextHolder;
 import com.usoftchina.saas.exception.BizException;
-import com.usoftchina.saas.money.mapper.RecbalanceMapper;
-import com.usoftchina.saas.money.mapper.RecbalancedetMapper;
-import com.usoftchina.saas.money.mapper.RecbalancedetailMapper;
+import com.usoftchina.saas.money.mapper.*;
 import com.usoftchina.saas.money.po.*;
 import com.usoftchina.saas.money.service.RecbalanceService;
 import com.usoftchina.saas.page.PageRequest;
@@ -38,6 +36,10 @@ public class RecbalanceServiceImpl implements RecbalanceService {
     RecbalancedetailMapper recbalancedetailMapper;
     @Autowired
     private MaxnumberService maxnumberService;
+    @Autowired
+    private BanksubledgerMapper banksubledgerMapper;
+    @Autowired
+    private SubledgerMapper subledgerMapper;
 
     @Override
     public DocBaseDTO insert(Rec rec) {
@@ -128,6 +130,13 @@ public class RecbalanceServiceImpl implements RecbalanceService {
         recbalance.setRb_status(Status.AUDITED.getDisplay());
         recbalance.setRb_statuscode(Status.AUDITED.name());
         recbalance.setCompanyId(companyId);
+        Subledger subledger = this.changSubledgerUntil(recbalance);
+        if (subledger.getSl_code() == null){
+            subledgerMapper.insertSelective(subledger);
+        }else {
+            subledgerMapper.updateByPrimaryKeySelective(subledger);
+        }
+        this.changBankUntil(rec);
         if ( recbalance == null || "".equals(recbalance)){
             this.insert(rec);
         }else {
@@ -142,6 +151,11 @@ public class RecbalanceServiceImpl implements RecbalanceService {
         recbalance.setRb_status(Status.UNAUDITED.getDisplay());
         recbalance.setRb_statuscode(Status.UNAUDITED.name());
         recbalanceMapper.updateByPrimaryKeySelective(recbalance);
+
+        //删除中间表
+        recbalance = recbalanceMapper.selectByPrimaryKey(id.intValue());
+        subledgerMapper.deleteByPrimaryKey(recbalance.getRb_code(), recbalance.getRb_kind());
+        banksubledgerMapper.deleteByPrimaryKey(recbalance.getRb_code(), recbalance.getRb_kind());
     }
 
     public PageInfo<Recbalance> selectList(PageRequest page, ListReqDTO reqDTO) {
@@ -176,6 +190,14 @@ public class RecbalanceServiceImpl implements RecbalanceService {
             recbalance.setId(Long.valueOf(docBaseDTO.getId()));
             recbalance.setRb_status(com.usoftchina.saas.commons.po.Status.AUDITED.getDisplay());
             recbalance.setRb_statuscode(Status.AUDITED.name());
+            Rec rec = this.select(Math.toIntExact(recbalance.getId()));
+            Subledger subledger = this.changSubledgerUntil(rec.getMain());
+            if (subledger.getSl_code() == null){
+                subledgerMapper.insertSelective(subledger);
+            }else {
+                subledgerMapper.updateByPrimaryKeySelective(subledger);
+            }
+            this.changBankUntil(rec);
             recbalanceMapper.updateByPrimaryKeySelective(recbalance);
         }
     }
@@ -223,4 +245,79 @@ public class RecbalanceServiceImpl implements RecbalanceService {
         }
         return recbalancesList;
     }
+
+    //插入中间表
+    public Subledger changSubledgerUntil(Recbalance recbalance){
+        Subledger subledger = new Subledger();
+        subledger.setCompanyId(BaseContextHolder.getCompanyId());
+        subledger.setSl_code(recbalance.getRb_code());
+        subledger.setSl_kind(recbalance.getRb_kind());
+        subledger.setSl_custid(recbalance.getRb_custid());
+        subledger.setSl_vendid(0);
+        subledger.setSl_date(recbalance.getRb_date());
+        if (recbalance.getRb_rdamount() == null){
+            subledger.setSl_amount((double) 0);
+        }else {
+            subledger.setSl_amount(recbalance.getRb_rdamount() * -1);
+        }
+        subledger.setSl_orderamount(recbalance.getRb_rdamount());
+
+        Double yamount = recbalance.getRb_preamount();
+        if (yamount == null){
+            yamount = Double.valueOf(0);
+        }
+        if (yamount > 0 || yamount < 0){
+            subledger.setSl_yamount(yamount);
+        }else {
+            subledger.setSl_yamount((double) 0);
+        }
+        if (subledger.getSl_orderamount() == null){
+            subledger.setSl_orderamount((double) 0);
+        }
+
+        if (subledger.getSl_discount() == null){
+            subledger.setSl_discount((double) 0);
+        }
+        subledger.setSl_namount(subledger.getSl_orderamount() + subledger.getSl_discount() - subledger.getSl_yamount());
+        subledger.setSl_remark(subledger.getSl_remark());
+        subledger.setSl_preamount(subledger.getSl_preamount());
+
+        return subledger;
+    }
+
+    //插入bank中间表
+    public void changBankUntil(Rec rec){
+        Banksubledger banksubledger = new Banksubledger();
+        Recbalance recbalance = rec.getMain();
+        List<Recbalancedet> recbalancedetList = rec.getItems1();
+        Iterator isList = recbalancedetList.iterator();
+        while (isList.hasNext()) {
+            Recbalancedet recbalancedet = (Recbalancedet) isList.next();
+            banksubledger.setCompanyId(BaseContextHolder.getCompanyId());
+            banksubledger.setBl_ym(recbalancedet.getRd_ym());
+            banksubledger.setBl_bankid(recbalancedet.getRd_bankid());
+            banksubledger.setBl_bankcode(recbalancedet.getRd_bankcode());
+            banksubledger.setBl_bankname(recbalancedet.getRd_bankname());
+            banksubledger.setBl_code(recbalance.getRb_code());
+            banksubledger.setBl_kind(recbalance.getRb_kind());
+            banksubledger.setBl_date(recbalance.getRb_date());
+
+            if (recbalance.getRb_custid() == null || recbalance.getRb_custid() != 0){
+                banksubledger.setBl_asstype("供应商往来");
+            }
+            banksubledger.setBl_assid(recbalance.getRb_custid());
+            banksubledger.setBl_asscode(recbalance.getRb_custcode());
+            banksubledger.setBl_assname(recbalance.getRb_custname());
+            banksubledger.setBl_spending(recbalancedet.getRd_amount());
+            banksubledger.setBl_manname(recbalance.getRb_manname());
+            banksubledger.setBl_remark(recbalancedet.getRd_remark());
+            banksubledger.setBl_orderamount(recbalancedet.getRd_amount());
+            if (banksubledgerMapper.selectCode(banksubledger.getBl_code()) == null){
+                banksubledgerMapper.insertSelective(banksubledger);
+            }else {
+                banksubledgerMapper.updateByPrimaryKeySelective(banksubledger);
+            }
+
+        }
+    }
 }

+ 8 - 4
applications/money/money-server/src/main/resources/mapper/BanksubledgerMapper.xml

@@ -403,11 +403,11 @@
             <if test="updaterId != null">
                 updaterId = #{updaterId,jdbcType=INTEGER},
             </if>
-            <if test="updatedate != null">
-                updatedate = #{updatedate,jdbcType=TIMESTAMP},
+            <if test="updateTime != null">
+                updatedate = #{updateTime,jdbcType=TIMESTAMP},
             </if>
         </set>
-        where bl_id = #{bl_id,jdbcType=INTEGER}
+        where bl_id = #{id,jdbcType=INTEGER}
     </update>
     <update id="updateByPrimaryKey" parameterType="com.usoftchina.saas.money.po.Banksubledger">
     update banksubledger
@@ -430,6 +430,10 @@
       updatedate = #{updatedate,jdbcType=TIMESTAMP}
     where bl_id = #{bl_id,jdbcType=INTEGER}
   </update>
-
+    <select id="selectCode" parameterType="java.lang.String" resultMap="BaseResultMap">
+        select bl_code
+        from banksubledger
+        where bl_code = #{bl_code,jdbcType=VARCHAR}
+    </select>
 
 </mapper>

+ 7 - 1
applications/money/money-server/src/main/resources/mapper/SubledgerMapper.xml

@@ -204,7 +204,7 @@
         sl_ym = #{sl_ym,jdbcType=INTEGER},
       </if>
     </set>
-    where sl_id = #{sl_id,jdbcType=INTEGER}
+    where sl_id = #{id,jdbcType=INTEGER}
   </update>
   <update id="updateByPrimaryKey" parameterType="com.usoftchina.saas.money.po.Subledger" >
     update subledger
@@ -241,4 +241,10 @@
     </where>  order by sl_id
   </select>
 
+  <select id="selectCode" parameterType="java.lang.String" resultMap="BaseResultMap">
+        select sl_code
+        from subledger
+        where sl_code = #{sl_code,jdbcType=VARCHAR}
+    </select>
+
 </mapper>

+ 3 - 0
applications/purchase/purchase-server/src/main/java/com/usoftchina/saas/purchase/service/impl/PurchaseServiceImpl.java

@@ -467,6 +467,7 @@ public class PurchaseServiceImpl extends CommonBaseServiceImpl<PurchaseMapper, P
         return Result.success(baseDTO);
     }
 
+    @Transactional
     private Result singleAudit(Long id) {
         DocBaseDTO docBaseDTO = getBaseDTOById(id);
         Result result = Result.success(docBaseDTO);
@@ -488,6 +489,7 @@ public class PurchaseServiceImpl extends CommonBaseServiceImpl<PurchaseMapper, P
         return result;
     }
 
+    @Transactional
     private void singleUnAudit(Long id) {
         Purchase purchase = new Purchase();
         //生成更新对象
@@ -503,6 +505,7 @@ public class PurchaseServiceImpl extends CommonBaseServiceImpl<PurchaseMapper, P
         messageLogService.unAudit(docBaseDTO);
     }
 
+    @Transactional
     private void singleDelete(Long id) {
         if (null != id) {
             //从表删除

+ 8 - 8
applications/purchase/purchase-server/src/main/resources/mapper/ProdInOutMapper.xml

@@ -19,7 +19,7 @@
     <result column="pi_total" jdbcType="DOUBLE" property="pi_total" />
     <result column="pi_recordmanid" jdbcType="INTEGER" property="pi_recordmanid" />
     <result column="pi_recordman" jdbcType="VARCHAR" property="pi_recordman" />
-    <result column="pi_recorddate" jdbcType="TIMESTAMP" property="pi_recorddate" />
+    <result column="pi_recorddate" jdbcType="TIMESTAMP" property="createTime" />
     <result column="pi_status" jdbcType="VARCHAR" property="pi_status" />
     <result column="pi_statuscode" jdbcType="VARCHAR" property="pi_statuscode" />
     <result column="pi_printstatus" jdbcType="VARCHAR" property="pi_printstatus" />
@@ -120,7 +120,7 @@
       <if test="pi_recordman != null">
         pi_recordman,
       </if>
-      <if test="pi_recorddate != null">
+      <if test="createTime != null">
         pi_recorddate,
       </if>
       <if test="pi_status != null">
@@ -213,8 +213,8 @@
       <if test="pi_recordman != null">
         #{pi_recordman,jdbcType=VARCHAR},
       </if>
-      <if test="pi_recorddate != null">
-        #{pi_recorddate,jdbcType=TIMESTAMP},
+      <if test="createTime != null">
+        #{createTime,jdbcType=TIMESTAMP},
       </if>
       <if test="pi_status != null">
         #{pi_status,jdbcType=VARCHAR},
@@ -309,8 +309,8 @@
       <if test="pi_recordman != null">
         pi_recordman = #{pi_recordman,jdbcType=VARCHAR},
       </if>
-      <if test="pi_recorddate != null">
-        pi_recorddate = #{pi_recorddate,jdbcType=TIMESTAMP},
+      <if test="createTime != null">
+        pi_recorddate = #{createTime,jdbcType=TIMESTAMP},
       </if>
       <if test="pi_status != null">
         pi_status = #{pi_status,jdbcType=VARCHAR},
@@ -373,7 +373,7 @@
       pi_total = #{pi_total,jdbcType=DOUBLE},
       pi_recordmanid = #{pi_recordmanid,jdbcType=INTEGER},
       pi_recordman = #{pi_recordman,jdbcType=VARCHAR},
-      pi_recorddate = #{pi_recorddate,jdbcType=TIMESTAMP},
+      pi_recorddate = #{createTime,jdbcType=TIMESTAMP},
       pi_status = #{pi_status,jdbcType=VARCHAR},
       pi_statuscode = #{pi_statuscode,jdbcType=VARCHAR},
       pi_printstatus = #{pi_printstatus,jdbcType=VARCHAR},
@@ -408,7 +408,7 @@
       pi_total = #{pi_total,jdbcType=DOUBLE},
       pi_recordmanid = #{pi_recordmanid,jdbcType=INTEGER},
       pi_recordman = #{pi_recordman,jdbcType=VARCHAR},
-      pi_recorddate = #{pi_recorddate,jdbcType=TIMESTAMP},
+      pi_recorddate = #{createTime,jdbcType=TIMESTAMP},
       pi_status = #{pi_status,jdbcType=VARCHAR},
       pi_statuscode = #{pi_statuscode,jdbcType=VARCHAR},
       pi_printstatus = #{pi_printstatus,jdbcType=VARCHAR},

+ 1 - 1
applications/sale/sale-dto/src/main/java/com/usoftchina/saas/sale/dto/ProdInOutDTO.java

@@ -71,6 +71,6 @@ public class ProdInOutDTO extends CommonBaseDTO implements Serializable {
 
     private String pi_address;
 
-
+    private String pi_remark;
 
 }

+ 1 - 0
applications/sale/sale-dto/src/main/java/com/usoftchina/saas/sale/dto/ProdInOutListDTO.java

@@ -137,4 +137,5 @@ public class ProdInOutListDTO extends CommonBaseDTO implements Serializable {
 
     private String pd_remark;
 
+    private String pi_remark;
 }

+ 2 - 0
applications/sale/sale-server/src/main/java/com/usoftchina/saas/sale/mapper/SaleMapper.java

@@ -37,4 +37,6 @@ public interface SaleMapper {
     void updateTotalAndNetPrice(Long id);
 
     void updateNetTotal(Long id);
+
+    Integer checkSendStatus(Long id);
 }

+ 6 - 4
applications/sale/sale-server/src/main/java/com/usoftchina/saas/sale/service/impl/ProdInOutServiceImpl.java

@@ -106,6 +106,7 @@ public class ProdInOutServiceImpl extends CommonBaseServiceImpl<ProdInOutMapper,
         prodInOut.setPi_said(main.getPi_said());
         prodInOut.setPi_sacode(main.getPi_sacode());
 
+        prodInOut.setPi_remark(main.getPi_remark());
         //判断更新与保存动作
         if (StringUtils.isEmpty(pi_id) || "0".equals(pi_id.toString())){
             //插入操作
@@ -404,8 +405,9 @@ public class ProdInOutServiceImpl extends CommonBaseServiceImpl<ProdInOutMapper,
         //插入销售退货单主表
         ProdInOut targetPi = new ProdInOut();
         //生成单号
-        String piInoutno  = maxnumberService.getMaxnumber(BillCodeSeq.SALEIN.getCaller(),true).getData();
-                //BillCodeSeq.SALEIN.getCaller()+Math.abs(Math.random()*100);
+        String piInoutno  =
+                maxnumberService.getMaxnumber(BillCodeSeq.SALEIN.getCaller(),true).getData();
+               // BillCodeSeq.SALEIN.getCaller()+Math.abs(Math.random()*100);
 
         //设置公司id
         targetPi.setCompanyId(sourcePi.getCompanyId());
@@ -576,8 +578,8 @@ public class ProdInOutServiceImpl extends CommonBaseServiceImpl<ProdInOutMapper,
         }else {//销售退货单
             caller = BillCodeSeq.SALEIN.getCaller();
         }
-        return  maxnumberService.pushMaxnubmer(count, code, caller).getData();
-        //return  caller+Math.abs(Math.random()*100);
+        return //code;
+                maxnumberService.pushMaxnubmer(count, code, caller).getData();
     }
 
     /**

+ 5 - 3
applications/sale/sale-server/src/main/java/com/usoftchina/saas/sale/service/impl/SaleServiceImpl.java

@@ -276,6 +276,11 @@ public class SaleServiceImpl implements SaleService{
         if (null != code) {
             throw new BizException(BizExceptionCode.SALE_NULL_BILL);
         }
+        //检测出货状态为未出货
+        Integer num = saleMapper.checkSendStatus(id);
+        if (num == 0) {
+            throw new BizException(BizExceptionCode.SALEOUT_UNAUDIT_ERROR);
+        }
         singleUnAudit(id);
     }
 
@@ -475,9 +480,6 @@ public class SaleServiceImpl implements SaleService{
             saledetailMapper.updateByPrimaryKeySelective(saleDetail);
 
         }
-        //更新主表入库状态
-        sale.setSa_sendstatus(Status.TURNOUT.getDisplay());
-        sale.setSa_sendstatuscode(Status.TURNOUT.name());
         //更新存在字段
         saleMapper.updateByPrimaryKeySelective(sale);
         prodIODetailMapper.updatePDSaleOut(pi_id);

+ 6 - 5
applications/sale/sale-server/src/main/resources/mapper/ProdInOutListMapper.xml

@@ -71,29 +71,30 @@
     <result column="pi_nettotal" jdbcType="DOUBLE" property="pi_nettotal" />
     <result column="pi_auditdate" jdbcType="TIMESTAMP" property="pi_auditdate" />
     <result column="pi_auditman" jdbcType="VARCHAR" property="pi_auditman" />
+    <result column="pi_remark" jdbcType="VARCHAR" property="pi_remark" />
   </resultMap>
 
 
   <select id="selectProdInOutListByCondition" resultMap="BaseResultMap">
-    select  *  from prodinout left join prodiodetail on pi_id = pd_piid
+    select  *  from prodinout
     <where>
       <if test="con != null">
         ${con}
       </if>
       <if test="companyId != null">
-        and   prodinout.companyId = #{companyId}
-      </if> order by pi_date desc,pi_id desc,pd_pdno
+        and companyId = #{companyId}
+      </if> order by pi_date desc,pi_id desc
     </where>
   </select>
 
   <select id="selectProdInOutBycondition"  resultMap="BaseResultMap">
-    select  *  from prodinout
+    select  *  from prodinout left join prodiodetail on pi_id = pd_piid left join product on pr_id = pd_prodid
       <where>
         <if test="con != null">
           ${con}
         </if>
         <if test="companyId != null">
-          and  companyId = #{companyId}
+          and  prodinout.companyId = #{companyId}
         </if>
       </where>
       order by pi_id desc

+ 19 - 7
applications/sale/sale-server/src/main/resources/mapper/ProdInOutMapper.xml

@@ -35,6 +35,7 @@
     <result column="pi_auditdate" jdbcType="TIMESTAMP" property="pi_auditdate" />
     <result column="pi_auditman" jdbcType="VARCHAR" property="pi_auditman" />
     <result column="pi_address" jdbcType="VARCHAR" property="pi_address" />
+    <result column="pi_remark" jdbcType="VARCHAR" property="pi_remark" />
   </resultMap>
   <resultMap extends="BaseResultMap" id="ResultMapWithBLOBs" type="com.usoftchina.saas.storage.po.ProdInOut">
     <result column="pi_address" jdbcType="LONGVARCHAR" property="pi_address" />
@@ -104,7 +105,7 @@
     companyid, updaterid, updatetime, pi_text1, pi_text2, pi_text3, pi_text4, pi_text5
   </sql>
   <sql id="Blob_Column_List">
-    pi_address
+    pi_address,pi_remark
   </sql>
     <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="ResultMapWithBLOBs">
     select 
@@ -129,7 +130,7 @@
       pi_printstatuscode, companyid, updaterid, 
       updatetime, pi_text1, pi_text2, 
       pi_text3, pi_text4, pi_text5,
-      pi_address)
+      pi_address,pi_remark)
     values (#{pi_id,jdbcType=INTEGER}, #{pi_inoutno,jdbcType=VARCHAR}, #{pi_class,jdbcType=VARCHAR}, 
       #{pi_date,jdbcType=TIMESTAMP}, #{pi_vendid,jdbcType=INTEGER}, #{pi_vendcode,jdbcType=VARCHAR}, 
       #{pi_vendname,jdbcType=VARCHAR}, #{pi_custid,jdbcType=INTEGER}, #{pi_custcode,jdbcType=VARCHAR}, 
@@ -140,7 +141,8 @@
       #{pi_printstatuscode,jdbcType=VARCHAR}, #{companyid,jdbcType=INTEGER}, #{updaterid,jdbcType=INTEGER}, 
       #{updatetime,jdbcType=TIMESTAMP}, #{pi_text1,jdbcType=VARCHAR}, #{pi_text2,jdbcType=VARCHAR}, 
       #{pi_text3,jdbcType=VARCHAR}, #{pi_text4,jdbcType=VARCHAR}, #{pi_text5,jdbcType=VARCHAR}, 
-      #{pi_address,jdbcType=LONGVARCHAR})
+      #{pi_address,jdbcType=LONGVARCHAR},
+      #{pi_remark,jdbcType=LONGVARCHAR})
   </insert>
   <insert id="insertSelective" parameterType="com.usoftchina.saas.storage.po.ProdInOut">
     <selectKey resultType="java.lang.Long" keyProperty="id">
@@ -239,6 +241,9 @@
       <if test="pi_address != null">
         pi_address,
       </if>
+      <if test="pi_remark != null">
+        pi_remark,
+      </if>
     </trim>
     <trim prefix="values (" suffix=")" suffixOverrides=",">
 
@@ -332,6 +337,9 @@
       <if test="pi_address != null">
         #{pi_address,jdbcType=LONGVARCHAR},
       </if>
+      <if test="pi_remark != null">
+        #{pi_remark,jdbcType=LONGVARCHAR},
+      </if>
     </trim>
   </insert>
   <update id="updateByPrimaryKeySelective" parameterType="com.usoftchina.saas.storage.po.ProdInOut">
@@ -427,7 +435,9 @@
       <if test="pi_address != null">
         pi_address = #{pi_address,jdbcType=LONGVARCHAR},
       </if>
-
+      <if test="pi_remark != null">
+        pi_remark = #{pi_remark,jdbcType=LONGVARCHAR},
+      </if>
     </set>
     where pi_id = #{id,jdbcType=INTEGER}
   </update>
@@ -462,8 +472,8 @@
       pi_text3 = #{pi_text3,jdbcType=VARCHAR},
       pi_text4 = #{pi_text4,jdbcType=VARCHAR},
       pi_text5 = #{pi_text5,jdbcType=VARCHAR},
-      pi_address = #{pi_address,jdbcType=LONGVARCHAR}
-
+      pi_address = #{pi_address,jdbcType=LONGVARCHAR},
+      pi_remark = #{pi_remark,jdbcType=LONGVARCHAR}
     where pi_id = #{pi_id,jdbcType=INTEGER}
   </update>
   <update id="updateByPrimaryKey" parameterType="com.usoftchina.saas.storage.po.ProdInOut">
@@ -496,7 +506,9 @@
       pi_text2 = #{pi_text2,jdbcType=VARCHAR},
       pi_text3 = #{pi_text3,jdbcType=VARCHAR},
       pi_text4 = #{pi_text4,jdbcType=VARCHAR},
-      pi_text5 = #{pi_text5,jdbcType=VARCHAR}
+      pi_text5 = #{pi_text5,jdbcType=VARCHAR},
+      pi_address = #{pi_address,jdbcType=LONGVARCHAR},
+      pi_remark = #{pi_remark,jdbcType=LONGVARCHAR}
     where pi_id = #{id,jdbcType=INTEGER}
   </update>
   <select id="selectCodeById" resultType="string" parameterType="long">

+ 5 - 0
applications/sale/sale-server/src/main/resources/mapper/SaleMapper.xml

@@ -383,4 +383,9 @@
     update saledetail a set sd_nettotal=round(ifnull(sd_netprice,0)*ifnull(sd_qty,0),2)
     where sd_said=#{id}
   </update>
+
+  <select id="checkSendStatus" parameterType="long" resultType="java.lang.Integer">
+    select count(1) from sale where sa_id = #{id}
+    and ifnull(sa_sendstatuscode,'UNTURNOUT') = 'UNTURNOUT';
+  </select>
 </mapper>

+ 8 - 0
applications/storage/storage-dto/src/main/java/com/usoftchina/saas/storage/dto/MakeListDTO.java

@@ -32,4 +32,12 @@ public class MakeListDTO extends CommonBaseDTO implements Serializable {
     public void setItems(List<MakeMaterial> items) {
         this.items = items;
     }
+
+    public MakeListDTO(Make main, List<MakeMaterial> items) {
+        this.main = main;
+        this.items = items;
+    }
+
+    public MakeListDTO() {
+    }
 }

+ 1 - 1
applications/storage/storage-dto/src/main/java/com/usoftchina/saas/storage/dto/ProdInOutDTO.java

@@ -73,5 +73,5 @@ public class ProdInOutDTO extends CommonBaseDTO implements Serializable {
 
     private Date pi_auditman;
 
-
+    private String pi_remark;
 }

+ 1 - 0
applications/storage/storage-dto/src/main/java/com/usoftchina/saas/storage/dto/ProdInOutListDTO.java

@@ -142,4 +142,5 @@ public class ProdInOutListDTO extends CommonBaseEntity implements Serializable {
 
     private String pd_remark;
 
+    private String pi_remark;
 }

+ 13 - 3
applications/storage/storage-dto/src/main/java/com/usoftchina/saas/storage/po/Make.java

@@ -14,7 +14,7 @@ public class Make extends CommonBaseEntity implements Serializable {
 
     private String ma_type;
 
-    private Integer ma_prodid;
+    private Long ma_prodid;
 
     private String ma_prodcode;
 
@@ -32,6 +32,8 @@ public class Make extends CommonBaseEntity implements Serializable {
 
     private String ma_whcode;
 
+    private String ma_whname;
+
     private Date ma_recorddate;
 
     private Integer ma_recordid;
@@ -52,6 +54,14 @@ public class Make extends CommonBaseEntity implements Serializable {
 
     private String ma_text5;
 
+    public String getMa_whname() {
+        return ma_whname;
+    }
+
+    public void setMa_whname(String ma_whname) {
+        this.ma_whname = ma_whname;
+    }
+
     public String getMa_code() {
         return ma_code;
     }
@@ -84,11 +94,11 @@ public class Make extends CommonBaseEntity implements Serializable {
         this.ma_type = ma_type == null ? null : ma_type.trim();
     }
 
-    public Integer getMa_prodid() {
+    public Long getMa_prodid() {
         return ma_prodid;
     }
 
-    public void setMa_prodid(Integer ma_prodid) {
+    public void setMa_prodid(Long ma_prodid) {
         this.ma_prodid = ma_prodid;
     }
 

+ 3 - 3
applications/storage/storage-dto/src/main/java/com/usoftchina/saas/storage/po/MakeMaterial.java

@@ -17,7 +17,7 @@ public class MakeMaterial extends CommonBaseEntity implements Serializable {
 
     private Integer mm_detno;
 
-    private Integer mm_prodid;
+    private Long mm_prodid;
 
     private String mm_prodcode;
 
@@ -73,11 +73,11 @@ public class MakeMaterial extends CommonBaseEntity implements Serializable {
         this.mm_detno = mm_detno;
     }
 
-    public Integer getMm_prodid() {
+    public Long getMm_prodid() {
         return mm_prodid;
     }
 
-    public void setMm_prodid(Integer mm_prodid) {
+    public void setMm_prodid(Long mm_prodid) {
         this.mm_prodid = mm_prodid;
     }
 

+ 1 - 1
applications/storage/storage-dto/src/main/java/com/usoftchina/saas/storage/po/ProdInOut.java

@@ -69,5 +69,5 @@ public class ProdInOut extends CommonBaseEntity implements Serializable {
 
     private String pi_auditman;
 
-
+    private String pi_remark;
 }

+ 1 - 0
applications/storage/storage-dto/src/main/java/com/usoftchina/saas/storage/po/ProdInOutList.java

@@ -144,4 +144,5 @@ public class ProdInOutList extends CommonBaseEntity{
 
     private Double pi_nettotal;
 
+    private String pi_remark;
 }

+ 29 - 0
applications/storage/storage-server/src/main/java/com/usoftchina/saas/storage/controller/MakeController.java

@@ -1,6 +1,7 @@
 package com.usoftchina.saas.storage.controller;
 
 import com.usoftchina.saas.base.Result;
+import com.usoftchina.saas.commons.dto.BatchDealBaseDTO;
 import com.usoftchina.saas.commons.dto.DocBaseDTO;
 import com.usoftchina.saas.commons.dto.ListReqDTO;
 import com.usoftchina.saas.page.PageRequest;
@@ -27,6 +28,18 @@ public class MakeController {
         return Result.success(makeListDTO);
     }
 
+    @PostMapping("/audit/{id}")
+    public Result audit(@PathVariable("id") Long id){
+        DocBaseDTO docBaseDTO = makeService.audit(id);
+        return Result.success(docBaseDTO);
+    }
+
+    @PostMapping("/unAudit/{id}")
+    public Result resAudit(@PathVariable("id") Long id){
+        DocBaseDTO docBaseDTO = makeService.resAudit(id);
+        return Result.success(docBaseDTO);
+    }
+
     @PostMapping("/save")
     public Result save(@RequestBody MakeListDTO makeListDTO){
         DocBaseDTO docBaseDTO = makeService.saveOrUpdate(makeListDTO);
@@ -56,4 +69,20 @@ public class MakeController {
         DocBaseDTO docBaseDTO = makeService.open(id);
         return Result.success(docBaseDTO);
     }
+
+    @PostMapping("/batchOpen")
+    public Result batchOpen(@RequestBody BatchDealBaseDTO batchDealBaseDTO){
+        return Result.success(makeService.batchOpen(batchDealBaseDTO));
+    }
+
+    @PostMapping("/batchClose")
+    public Result batchClose(@RequestBody BatchDealBaseDTO batchDealBaseDTO){
+        return Result.success(makeService.batchClose(batchDealBaseDTO));
+    }
+
+    @PostMapping("/batchDelete")
+    public Result batchDelete(@RequestBody BatchDealBaseDTO batchDealBaseDTO){
+        makeService.batchDelete(batchDealBaseDTO);
+        return Result.success();
+    }
 }

+ 2 - 0
applications/storage/storage-server/src/main/java/com/usoftchina/saas/storage/mapper/MakeMapper.java

@@ -26,4 +26,6 @@ public interface MakeMapper extends CommonBaseMapper<Make> {
     int validateCodeWhenUpdate(@Param("code") String code, @Param("id") Long id, @Param("companyId") Long company);
 
     double getOnHand(@Param("prodcode") String prodcode, @Param("whcode") String whcode, @Param("companyId") Long companyId);
+
+    String selectMakeInOutCode(@Param("code") String code, @Param("companyId") Long companyId, @Param("type") String type);
 }

+ 36 - 0
applications/storage/storage-server/src/main/java/com/usoftchina/saas/storage/service/MakeService.java

@@ -3,6 +3,7 @@ package com.usoftchina.saas.storage.service;
 
 import com.github.pagehelper.PageInfo;
 import com.usoftchina.saas.base.service.CommonBaseService;
+import com.usoftchina.saas.commons.dto.BatchDealBaseDTO;
 import com.usoftchina.saas.commons.dto.DocBaseDTO;
 import com.usoftchina.saas.commons.dto.ListReqDTO;
 import com.usoftchina.saas.page.PageRequest;
@@ -64,4 +65,39 @@ public interface MakeService extends CommonBaseService<MakeMapper, Make> {
      * @return
      */
     boolean deleteDetail(Long id);
+
+    /**
+     * 审核
+     * @param id
+     * @return
+     */
+    DocBaseDTO audit(Long id);
+
+    /**
+     * 反审核
+     * @param id
+     * @return
+     */
+    DocBaseDTO resAudit(Long id);
+
+    /**
+     * 批量关闭
+     * @param batchDealBaseDTO
+     * @return
+     */
+    String batchClose(BatchDealBaseDTO batchDealBaseDTO);
+
+    /**
+     * 批量开启
+     * @param batchDealBaseDTO
+     * @return
+     */
+    String batchOpen(BatchDealBaseDTO batchDealBaseDTO);
+
+    /**
+     * 批量删除
+     * @param batchDealBaseDTO
+     * @return
+     */
+    boolean batchDelete(BatchDealBaseDTO batchDealBaseDTO);
 }

+ 355 - 6
applications/storage/storage-server/src/main/java/com/usoftchina/saas/storage/service/impl/MakeServiceImpl.java

@@ -2,30 +2,36 @@ package com.usoftchina.saas.storage.service.impl;
 
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
+import com.usoftchina.saas.base.Result;
 import com.usoftchina.saas.base.service.CommonBaseServiceImpl;
 import com.usoftchina.saas.commons.api.MaxnumberService;
 import com.usoftchina.saas.commons.api.MessageLogService;
+import com.usoftchina.saas.commons.dto.BatchDealBaseDTO;
 import com.usoftchina.saas.commons.dto.DocBaseDTO;
 import com.usoftchina.saas.commons.dto.ListReqDTO;
 import com.usoftchina.saas.commons.exception.BizExceptionCode;
 import com.usoftchina.saas.commons.po.BillCodeSeq;
 import com.usoftchina.saas.commons.po.Status;
 import com.usoftchina.saas.context.BaseContextHolder;
+import com.usoftchina.saas.document.api.WarehouseApi;
 import com.usoftchina.saas.exception.BizException;
 import com.usoftchina.saas.page.PageRequest;
 import com.usoftchina.saas.storage.dto.MakeListDTO;
 import com.usoftchina.saas.storage.mapper.MakeMapper;
 import com.usoftchina.saas.storage.mapper.MakeMaterialMapper;
+import com.usoftchina.saas.storage.mapper.ProdIODetailMapper;
+import com.usoftchina.saas.storage.mapper.ProdInOutMapper;
 import com.usoftchina.saas.storage.po.Make;
 import com.usoftchina.saas.storage.po.MakeMaterial;
+import com.usoftchina.saas.storage.po.ProdIODetail;
+import com.usoftchina.saas.storage.po.ProdInOut;
 import com.usoftchina.saas.storage.service.MakeService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
 
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
+import java.util.*;
 
 @Service
 public class MakeServiceImpl extends CommonBaseServiceImpl<MakeMapper, Make> implements MakeService {
@@ -36,6 +42,12 @@ public class MakeServiceImpl extends CommonBaseServiceImpl<MakeMapper, Make> imp
     private MaxnumberService maxnumberService;
     @Autowired
     private MessageLogService messageLogService;
+    @Autowired
+    private ProdInOutMapper prodInOutMapper;
+    @Autowired
+    private ProdIODetailMapper prodIODetailMapper;
+    @Autowired
+    private WarehouseApi warehouseApi;
 
     @Override
     public PageInfo<Make> getList(PageRequest page, ListReqDTO listReqDTO) {
@@ -66,9 +78,6 @@ public class MakeServiceImpl extends CommonBaseServiceImpl<MakeMapper, Make> imp
     @Override
     @Transactional
     public DocBaseDTO saveOrUpdate(MakeListDTO makeListDTO) {
-        //校验库存是否足够
-        validStorage(makeListDTO);
-
         Make make = makeListDTO.getMain();
         List<MakeMaterial> makeMaterialList = makeListDTO.getItems();
         DocBaseDTO docBaseDTO = null;
@@ -217,6 +226,346 @@ public class MakeServiceImpl extends CommonBaseServiceImpl<MakeMapper, Make> imp
         return true;
     }
 
+    @Override
+    @Transactional
+    public DocBaseDTO audit(Long id) {
+        //1.获取主从表数据
+        Make make = getMapper().selectByPrimaryKey(id);
+        List<MakeMaterial> items = makeMaterialMapper.selectByFK(id, BaseContextHolder.getCompanyId());
+        MakeListDTO makeListDTO = new MakeListDTO(make, items);
+        //2.校验库存是否足够
+        validStorage(makeListDTO);
+        //3.生成  完工入库单和领料单 并 过账
+        generateProdIO(makeListDTO);
+        //4.修改单据状态
+        Make updateMake = new Make();
+        make.setMa_status(Status.AUDITED.getDisplay());
+        make.setMa_statuscode(Status.AUDITED.name());
+        make.setUpdateTime(new Date());
+        make.setUpdaterId(BaseContextHolder.getUserId());
+        DocBaseDTO docBaseDTO = generateMsgObj(id, make.getMa_code());
+        //5.记录LOG
+        messageLogService.audit(docBaseDTO);
+        return docBaseDTO;
+    }
+
+    /**
+     * 1.生成  完工入库单和生产领料单
+     * 2.过账
+     * @param makeListDTO
+     */
+    private void generateProdIO(MakeListDTO makeListDTO) {
+        Make make = makeListDTO.getMain();
+        List<MakeMaterial> items = makeListDTO.getItems();
+        if ("组装".equals(make.getMa_type())){
+            /** 1.生产领料单 **/
+            //a.主表
+            ProdInOut prodInOut = new ProdInOut();
+            prodInOut.setPi_class("生产领料单");
+            String code = maxnumberService.getMaxnumber("MakeOut", true).getData();
+            prodInOut.setPi_inoutno(code);
+            prodInOut.setPi_date(new Date());       //出入库日期
+            //prodInOut.setPi_total(make.getMa_price());
+            prodInOut.setPi_recordmanid(BaseContextHolder.getUserId());     //录入人ID
+            prodInOut.setPi_recorddate(new Date());     //录入日期
+            prodInOut.setPi_auditdate(new Date());      //审核日期
+            prodInOut.setPi_auditman("");               //审核人
+            prodInOut.setPi_status(Status.UNAUDITED.getDisplay());  //单据状态
+            prodInOut.setPi_statuscode(Status.UNAUDITED.name());    //单据状态码
+            prodInOut.setPi_text1(make.getMa_code());       //自定义字段1   赋值为制造单号
+            prodInOut.setCompanyId(BaseContextHolder.getCompanyId());   //公司ID
+            prodInOut.setCreateTime(new Date());
+            prodInOut.setCreatorId(BaseContextHolder.getUserId());      //创建人ID
+            prodInOutMapper.insertSelective(prodInOut);
+            long pi_id = prodInOut.getId();
+            //b.从表
+            List<ProdIODetail> prodIODetailList = new ArrayList<ProdIODetail>();
+            int detno = 0;
+            for (MakeMaterial item : items){
+                detno++;
+                ProdIODetail prodIODetail = new ProdIODetail();
+                prodIODetail.setPd_piid(pi_id);     //主表ID
+                prodIODetail.setPd_piclass("生产领料单");
+                prodIODetail.setPd_inoutno(code);       //出入库单号
+                prodIODetail.setPd_pdno(detno);         //出入库单序号
+                prodIODetail.setPd_ordercode(make.getMa_code());    //订单号
+                prodIODetail.setPd_orderdetno(item.getMm_detno());  //订单序号
+                prodIODetail.setPd_prodid(item.getMm_prodid());    //物料ID
+                prodIODetail.setPd_prodcode(item.getMm_prodcode()); //物料CODE
+                prodIODetail.setPd_outqty(item.getMm_qty());        //出库数
+                prodIODetail.setPd_price(item.getMm_price());       //成本单价 ???
+                prodIODetail.setPd_total(item.getMm_amount());      //金额
+                prodIODetail.setPd_whid(item.getMm_whid());         //仓库ID
+                prodIODetail.setPd_whcode(item.getMm_whcode());     //仓库CODE
+                prodIODetail.setPd_whname(item.getMm_whname());     //仓库名称
+                prodIODetail.setCompanyId(BaseContextHolder.getCompanyId());    //公司ID
+                prodIODetail.setPd_status(0);      //单据状态
+                prodIODetailList.add(prodIODetail);
+            }
+            prodIODetailMapper.batchInsert(prodIODetailList);
+
+            //c.领料单过账
+            Map<String, Object> map = new HashMap<String, Object>();
+            map.put("inoutNo", code);
+            map.put("class", "生产领料单");
+            map.put("commitid", BaseContextHolder.getUserId());
+            map.put("companyId", BaseContextHolder.getCompanyId());
+            map.put("result", null);
+            Result result = warehouseApi.post(map);
+            if (!StringUtils.isEmpty(result.getData())){
+                throw new BizException(76201, result.getData().toString());
+            }
+            /** 2.完工入库单 **/
+            //a.主表
+            prodInOut = new ProdInOut();
+            prodInOut.setPi_class("完工入库单");
+            code = maxnumberService.getMaxnumber("MakeIn", true).getData();
+            prodInOut.setPi_inoutno(code);
+            prodInOut.setPi_date(new Date());       //出入库日期
+            //prodInOut.setPi_total(make.getMa_price());
+            prodInOut.setPi_recordmanid(BaseContextHolder.getUserId());     //录入人ID
+            prodInOut.setPi_recorddate(new Date());     //录入日期
+            prodInOut.setPi_auditdate(new Date());      //审核日期
+            prodInOut.setPi_auditman("");               //审核人
+            prodInOut.setPi_status(Status.UNAUDITED.getDisplay());  //单据状态
+            prodInOut.setPi_statuscode(Status.UNAUDITED.name());    //单据状态码
+            prodInOut.setPi_text1(make.getMa_code());       //自定义字段1   赋值为制造单号
+            prodInOut.setCompanyId(BaseContextHolder.getCompanyId());   //公司ID
+            prodInOut.setCreateTime(new Date());
+            prodInOut.setCreatorId(BaseContextHolder.getUserId());      //创建人ID
+            prodInOutMapper.insertSelective(prodInOut);
+            pi_id = prodInOut.getId();
+            //b.从表
+            ProdIODetail prodIODetail = new ProdIODetail();
+            prodIODetail.setPd_piid(pi_id);     //主表ID
+            prodIODetail.setPd_piclass("完工入库单");
+            prodIODetail.setPd_inoutno(code);       //出入库单号
+            prodIODetail.setPd_pdno(1);         //出入库单序号
+            prodIODetail.setPd_ordercode(make.getMa_code());    //订单号
+            prodIODetail.setPd_orderdetno(1);  //订单序号
+            prodIODetail.setPd_prodid(make.getMa_prodid());    //物料ID
+            prodIODetail.setPd_prodcode(make.getMa_prodcode()); //物料CODE
+            prodIODetail.setPd_inqty(make.getMa_qty());        //入库数
+            prodIODetail.setPd_price(make.getMa_price());       //成本单价 ???
+            prodIODetail.setPd_total(make.getMa_total());      //金额
+            prodIODetail.setPd_whid(make.getMa_whid());         //仓库ID
+            prodIODetail.setPd_whcode(make.getMa_whcode());     //仓库CODE
+            prodIODetail.setPd_whname(make.getMa_whname());     //仓库名称
+            prodIODetail.setCompanyId(BaseContextHolder.getCompanyId());    //公司ID
+            prodIODetail.setPd_status(0);      //单据状态
+            prodIODetailMapper.insertSelective(prodIODetail);
+
+            map.put("inoutNo", code);
+            map.put("class", "完工入库单");
+            map.put("commitid", BaseContextHolder.getUserId());
+            map.put("companyId", BaseContextHolder.getCompanyId());
+            map.put("result", null);
+            result = warehouseApi.post(map);
+            if (!StringUtils.isEmpty(result.getData())){
+                throw new BizException(76202, result.getData().toString());
+            }
+        }else if ("拆件".equals(make.getMa_type())){
+            /** 1.生产领料单 **/
+            //a.主表
+            ProdInOut prodInOut = new ProdInOut();
+            prodInOut.setPi_class("生产领料单");
+            String code = maxnumberService.getMaxnumber("MakeOut", true).getData();
+            prodInOut.setPi_inoutno(code);
+            prodInOut.setPi_date(new Date());       //出入库日期
+            //prodInOut.setPi_total(make.getMa_price());
+            prodInOut.setPi_recordmanid(BaseContextHolder.getUserId());     //录入人ID
+            prodInOut.setPi_recorddate(new Date());     //录入日期
+            prodInOut.setPi_auditdate(new Date());      //审核日期
+            prodInOut.setPi_auditman("");               //审核人
+            prodInOut.setPi_status(Status.UNAUDITED.getDisplay());  //单据状态
+            prodInOut.setPi_statuscode(Status.UNAUDITED.name());    //单据状态码
+            prodInOut.setPi_text1(make.getMa_code());       //自定义字段1   赋值为制造单号
+            prodInOut.setCompanyId(BaseContextHolder.getCompanyId());   //公司ID
+            prodInOut.setCreateTime(new Date());
+            prodInOut.setCreatorId(BaseContextHolder.getUserId());      //创建人ID
+            prodInOutMapper.insertSelective(prodInOut);
+            long pi_id = prodInOut.getId();
+            //b.从表
+            ProdIODetail prodIODetail = new ProdIODetail();
+            prodIODetail.setPd_piid(pi_id);     //主表ID
+            prodIODetail.setPd_piclass("生产领料单");
+            prodIODetail.setPd_inoutno(code);       //出入库单号
+            prodIODetail.setPd_pdno(1);         //出入库单序号
+            prodIODetail.setPd_ordercode(make.getMa_code());    //订单号
+            prodIODetail.setPd_orderdetno(1);  //订单序号
+            prodIODetail.setPd_prodid(make.getMa_prodid());    //物料ID
+            prodIODetail.setPd_prodcode(make.getMa_code()); //物料CODE
+            prodIODetail.setPd_outqty(make.getMa_qty());        //出库数
+            prodIODetail.setPd_price(make.getMa_price());       //成本单价 ???
+            prodIODetail.setPd_total(make.getMa_total());      //金额
+            prodIODetail.setPd_whid(make.getMa_whid());         //仓库ID
+            prodIODetail.setPd_whcode(make.getMa_whcode());     //仓库CODE
+            prodIODetail.setPd_whname(make.getMa_whname());     //仓库名称
+            prodIODetail.setCompanyId(BaseContextHolder.getCompanyId());    //公司ID
+            prodIODetail.setPd_status(0);      //单据状态
+            prodIODetailMapper.insertSelective(prodIODetail);
+
+            //c.领料单过账
+            Map<String, Object> map = new HashMap<String, Object>();
+            map.put("inoutNo", code);
+            map.put("class", "生产领料单");
+            map.put("commitid", BaseContextHolder.getUserId());
+            map.put("companyId", BaseContextHolder.getCompanyId());
+            map.put("result", null);
+            Result result = warehouseApi.post(map);
+            if (!StringUtils.isEmpty(result.getData())){
+                throw new BizException(76201, result.getData().toString());
+            }
+            /** 2.完工入库单 **/
+            //a.主表
+            prodInOut = new ProdInOut();
+            prodInOut.setPi_class("完工入库单");
+            code = maxnumberService.getMaxnumber("MakeIn", true).getData();
+            prodInOut.setPi_inoutno(code);
+            prodInOut.setPi_date(new Date());       //出入库日期
+            //prodInOut.setPi_total(make.getMa_price());
+            prodInOut.setPi_recordmanid(BaseContextHolder.getUserId());     //录入人ID
+            prodInOut.setPi_recorddate(new Date());     //录入日期
+            prodInOut.setPi_auditdate(new Date());      //审核日期
+            prodInOut.setPi_auditman("");               //审核人
+            prodInOut.setPi_status(Status.UNAUDITED.getDisplay());  //单据状态
+            prodInOut.setPi_statuscode(Status.UNAUDITED.name());    //单据状态码
+            prodInOut.setPi_text1(make.getMa_code());       //自定义字段1   赋值为制造单号
+            prodInOut.setCompanyId(BaseContextHolder.getCompanyId());   //公司ID
+            prodInOut.setCreateTime(new Date());
+            prodInOut.setCreatorId(BaseContextHolder.getUserId());      //创建人ID
+            prodInOutMapper.insertSelective(prodInOut);
+            pi_id = prodInOut.getId();
+            //b.从表
+            List<ProdIODetail> prodIODetailList = new ArrayList<ProdIODetail>();
+            int detno = 0;
+            for (MakeMaterial item : items) {
+                detno++;
+                prodIODetail = new ProdIODetail();
+                prodIODetail.setPd_piid(pi_id);     //主表ID
+                prodIODetail.setPd_piclass("完工入库单");
+                prodIODetail.setPd_inoutno(code);       //出入库单号
+                prodIODetail.setPd_pdno(detno);         //出入库单序号
+                prodIODetail.setPd_ordercode(make.getMa_code());    //订单号
+                prodIODetail.setPd_orderdetno(detno);  //订单序号
+                prodIODetail.setPd_prodid(item.getMm_prodid());    //物料ID
+                prodIODetail.setPd_prodcode(item.getMm_prodcode()); //物料CODE
+                prodIODetail.setPd_inqty(item.getMm_qty());        //入库数
+                prodIODetail.setPd_price(item.getMm_price());       //成本单价 ???
+                prodIODetail.setPd_total(item.getMm_amount());      //金额
+                prodIODetail.setPd_whid(item.getMm_whid());         //仓库ID
+                prodIODetail.setPd_whcode(item.getMm_whcode());     //仓库CODE
+                prodIODetail.setPd_whname(item.getMm_whname());     //仓库名称
+                prodIODetail.setCompanyId(BaseContextHolder.getCompanyId());    //公司ID
+                prodIODetail.setPd_status(0);      //单据状态
+                prodIODetailList.add(prodIODetail);
+            }
+            prodIODetailMapper.batchInsert(prodIODetailList);
+
+            map.put("inoutNo", code);
+            map.put("class", "完工入库单");
+            map.put("commitid", BaseContextHolder.getUserId());
+            map.put("companyId", BaseContextHolder.getCompanyId());
+            map.put("result", null);
+            result = warehouseApi.post(map);
+            if (!StringUtils.isEmpty(result.getData())){
+                throw new BizException(76202, result.getData().toString());
+            }
+        }
+    }
+
+    @Override
+    @Transactional
+    public DocBaseDTO resAudit(Long id) {
+        //1.获取主从表数据
+        Make make = getMapper().selectByPrimaryKey(id);
+        List<MakeMaterial> items = makeMaterialMapper.selectByFK(id, BaseContextHolder.getCompanyId());
+        MakeListDTO makeListDTO = new MakeListDTO(make, items);
+        //2.校验库存是否足够
+        validStorage(makeListDTO);
+        //3.查找  完工入库单和领料单
+        //4.反过账
+        selectProdIO(make);
+        //5.修改单据状态
+        Make updateMake = new Make();
+        make.setMa_status(Status.UNAUDITED.getDisplay());
+        make.setMa_statuscode(Status.UNAUDITED.name());
+        make.setUpdateTime(new Date());
+        make.setUpdaterId(BaseContextHolder.getUserId());
+        DocBaseDTO docBaseDTO = generateMsgObj(id, make.getMa_code());
+        //6.记录LOG
+        messageLogService.unAudit(docBaseDTO);
+        return docBaseDTO;
+    }
+
+    @Override
+    public String batchClose(BatchDealBaseDTO batchDealBaseDTO) {
+        StringBuilder errorMsg = new StringBuilder();
+        for (DocBaseDTO docBaseDTO : batchDealBaseDTO.getBaseDTOs()){
+            try{
+                close(docBaseDTO.getId());
+            }catch (Exception e){
+                errorMsg.append("编号:" + docBaseDTO.getCode() + "处理失败," + e.getMessage());
+            }
+        }
+        return errorMsg.toString();
+    }
+
+    @Override
+    public String batchOpen(BatchDealBaseDTO batchDealBaseDTO) {
+        StringBuilder errorMsg = new StringBuilder();
+        for (DocBaseDTO docBaseDTO : batchDealBaseDTO.getBaseDTOs()){
+            try{
+                open(docBaseDTO.getId());
+            }catch (Exception e){
+                errorMsg.append("编号:" + docBaseDTO.getCode() + "处理失败," + e.getMessage());
+            }
+        }
+        return errorMsg.toString();
+    }
+
+    @Override
+    public boolean batchDelete(BatchDealBaseDTO batchDealBaseDTO) {
+        if (null == batchDealBaseDTO || null == batchDealBaseDTO.getBaseDTOs() ||
+                batchDealBaseDTO.getBaseDTOs().size() == 0) {
+            return false;
+        }
+        for (DocBaseDTO docBaseDTO : batchDealBaseDTO.getBaseDTOs()){
+            delete(docBaseDTO.getId());
+        }
+        return true;
+    }
+
+    /**
+     * 1.查找制造单关联的出入库单据
+     * 2.反过账
+     * @param make
+     */
+    private void selectProdIO(Make make) {
+        String code = make.getMa_code();
+        Long companyId = BaseContextHolder.getCompanyId();
+        String InCode = getMapper().selectMakeInOutCode(code, companyId, "完工入库单");
+        String outCode = getMapper().selectMakeInOutCode(code, companyId, "生产领料单");
+        Map<String, Object> map = new HashMap<String, Object>();
+        map.put("inoutNo", InCode);
+        map.put("class", "完工入库单");
+        map.put("commitid", BaseContextHolder.getUserId());
+        map.put("companyId", companyId);
+        map.put("result", null);
+        //完工入库单反过账
+        Result result = warehouseApi.unPost(map);
+        if(!StringUtils.isEmpty(result)){
+            throw new BizException(76203, result.getData().toString());
+        }
+        map.put("inoutNo", outCode);
+        map.put("class", "生产领料单");
+        //生产领料单反过账
+        result = warehouseApi.unPost(map);
+        if(!StringUtils.isEmpty(result)){
+            throw new BizException(76204, result.getData().toString());
+        }
+    }
+
     /**
      * 构造日记记录对象
      * @param id

+ 18 - 3
applications/storage/storage-server/src/main/resources/mapper/MakeMapper.xml

@@ -16,6 +16,7 @@
     <result column="ma_qty" property="ma_qty" jdbcType="DOUBLE" />
     <result column="ma_whid" property="ma_whid" jdbcType="INTEGER" />
     <result column="ma_whcode" property="ma_whcode" jdbcType="VARCHAR" />
+    <result column="ma_whname" property="ma_whname" jdbcType="VARCHAR" />
     <result column="ma_recorddate" property="ma_recorddate" jdbcType="TIMESTAMP" />
     <result column="ma_recordid" property="ma_recordid" jdbcType="INTEGER" />
     <result column="ma_recorder" property="ma_recorder" jdbcType="VARCHAR" />
@@ -32,7 +33,7 @@
   </resultMap>
   <sql id="Base_Column_List" >
     ma_id, ma_code, ma_status, ma_statuscode, ma_type, ma_prodid, ma_prodcode, ma_proddetail, 
-    ma_prodspec, ma_version, ma_produnit, ma_qty, ma_whid, ma_whcode, ma_recorddate, 
+    ma_prodspec, ma_version, ma_produnit, ma_qty, ma_whid, ma_whcode, ma_whname, ma_recorddate,
     ma_recordid, ma_recorder, ma_price, ma_total, companyId, updaterId, updateTime, ma_text1, 
     ma_text2, ma_text3, ma_text4, ma_text5
   </sql>
@@ -54,7 +55,7 @@
       ma_statuscode, ma_type, ma_prodid, 
       ma_prodcode, ma_proddetail, ma_prodspec, 
       ma_version, ma_produnit, ma_qty, 
-      ma_whid, ma_whcode, ma_recorddate, 
+      ma_whid, ma_whcode, ma_whname, ma_recorddate,
       ma_recordid, ma_recorder, ma_price, 
       ma_total, companyId, updaterId, 
       updateTime, ma_text1, ma_text2, 
@@ -64,7 +65,7 @@
       #{ma_statuscode,jdbcType=VARCHAR}, #{ma_type,jdbcType=VARCHAR}, #{ma_prodid,jdbcType=INTEGER}, 
       #{ma_prodcode,jdbcType=VARCHAR}, #{ma_proddetail,jdbcType=VARCHAR}, #{ma_prodspec,jdbcType=VARCHAR}, 
       #{ma_version,jdbcType=VARCHAR}, #{ma_produnit,jdbcType=VARCHAR}, #{ma_qty,jdbcType=DOUBLE}, 
-      #{ma_whid,jdbcType=INTEGER}, #{ma_whcode,jdbcType=VARCHAR}, #{ma_recorddate,jdbcType=TIMESTAMP}, 
+      #{ma_whid,jdbcType=INTEGER}, #{ma_whcode,jdbcType=VARCHAR}, #{ma_whname,jdbcType=VARCHAR}, #{ma_recorddate,jdbcType=TIMESTAMP},
       #{ma_recordid,jdbcType=INTEGER}, #{ma_recorder,jdbcType=VARCHAR}, #{ma_price,jdbcType=DOUBLE}, 
       #{ma_total,jdbcType=DOUBLE}, #{companyId,jdbcType=INTEGER}, #{updaterId,jdbcType=INTEGER}, 
       #{updateTime,jdbcType=TIMESTAMP}, #{ma_text1,jdbcType=VARCHAR}, #{ma_text2,jdbcType=VARCHAR}, 
@@ -116,6 +117,9 @@
       <if test="ma_whcode != null" >
         ma_whcode,
       </if>
+      <if test="ma_whname != null" >
+          ma_whname,
+      </if>
       <if test="ma_recorddate != null" >
         ma_recorddate,
       </if>
@@ -196,6 +200,9 @@
       <if test="ma_whcode != null" >
         #{ma_whcode,jdbcType=VARCHAR},
       </if>
+      <if test="ma_whname != null" >
+          #{ma_whname,jdbcType=VARCHAR},
+      </if>
       <if test="ma_recorddate != null" >
         #{ma_recorddate,jdbcType=TIMESTAMP},
       </if>
@@ -279,6 +286,9 @@
       <if test="ma_whcode != null" >
         ma_whcode = #{ma_whcode,jdbcType=VARCHAR},
       </if>
+      <if test="ma_whname != null" >
+        ma_whname = #{ma_whname,jdbcType=VARCHAR},
+      </if>
       <if test="ma_recorddate != null" >
         ma_recorddate = #{ma_recorddate,jdbcType=TIMESTAMP},
       </if>
@@ -336,6 +346,7 @@
       ma_qty = #{ma_qty,jdbcType=DOUBLE},
       ma_whid = #{ma_whid,jdbcType=INTEGER},
       ma_whcode = #{ma_whcode,jdbcType=VARCHAR},
+      ma_whname = #{ma_whname,jdbcType=VARCHAR},
       ma_recorddate = #{ma_recorddate,jdbcType=TIMESTAMP},
       ma_recordid = #{ma_recordid,jdbcType=INTEGER},
       ma_recorder = #{ma_recorder,jdbcType=VARCHAR},
@@ -371,4 +382,8 @@
     <select id="getOnHand" resultType="double">
         SELECT PW_ONHAND FROM PRODUCTWH WHERE PW_PRODCODE=#{prodcode} AND PW_WHCODE=#{whcode} and COMPANYID=#{companyId}
     </select>
+
+    <select id="selectMakeInOutCode" resultType="string">
+        SELECT PI_INOUTNO FROM PRODINOUT WHERE COMPANYID=#{companyId} AND PI_TEXT1=#{code} AND PI_CLASS=#{type}
+    </select>
 </mapper>

+ 21 - 7
applications/storage/storage-server/src/main/resources/mapper/MakematerialMapper.xml

@@ -9,6 +9,7 @@
     <result column="mm_prodcode" property="mm_prodcode" jdbcType="VARCHAR" />
     <result column="mm_whid" property="mm_whid" jdbcType="INTEGER" />
     <result column="mm_whcode" property="mm_whcode" jdbcType="VARCHAR" />
+    <result column="mm_whname" property="mm_whname" jdbcType="VARCHAR" />
     <result column="mm_price" property="mm_price" jdbcType="DOUBLE" />
     <result column="mm_oneuseqty" property="mm_oneuseqty" jdbcType="DOUBLE" />
     <result column="mm_qty" property="mm_qty" jdbcType="DOUBLE" />
@@ -51,7 +52,7 @@
     </association>
   </resultMap>
   <sql id="Base_Column_List" >
-    mm_id, mm_maid, mm_detno, mm_prodid, mm_prodcode, mm_whid, mm_whcode, mm_price, mm_oneuseqty, 
+    mm_id, mm_maid, mm_detno, mm_prodid, mm_prodcode, mm_whid, mm_whcode, mm_whname, mm_price, mm_oneuseqty,
     mm_qty, mm_amount, mm_repprodcode, mm_remark, companyId, updaterId, updateTime
   </sql>
   <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
@@ -67,13 +68,13 @@
   <insert id="insert" parameterType="com.usoftchina.saas.storage.po.MakeMaterial" >
     insert into MakeMaterial (mm_maid, mm_detno,
       mm_prodid, mm_prodcode, mm_whid, 
-      mm_whcode, mm_price, mm_oneuseqty, 
+      mm_whcode, mm_whname, mm_price, mm_oneuseqty,
       mm_qty, mm_amount, mm_repprodcode, 
       mm_remark, companyId, updaterId, 
       updateTime)
     values (#{mm_maid}, #{mm_detno,jdbcType=INTEGER},
       #{mm_prodid,jdbcType=INTEGER}, #{mm_prodcode,jdbcType=VARCHAR}, #{mm_whid,jdbcType=INTEGER}, 
-      #{mm_whcode,jdbcType=VARCHAR}, #{mm_price,jdbcType=DOUBLE}, #{mm_oneuseqty,jdbcType=DOUBLE}, 
+      #{mm_whcode,jdbcType=VARCHAR}, #{mm_whname,jdbcType=VARCHAR}, #{mm_price,jdbcType=DOUBLE}, #{mm_oneuseqty,jdbcType=DOUBLE},
       #{mm_qty,jdbcType=DOUBLE}, #{mm_amount,jdbcType=DOUBLE}, #{mm_repprodcode,jdbcType=VARCHAR}, 
       #{mm_remark,jdbcType=VARCHAR}, #{companyId,jdbcType=INTEGER}, #{updaterId,jdbcType=INTEGER}, 
       #{updateTime,jdbcType=TIMESTAMP})
@@ -99,6 +100,9 @@
       <if test="mm_whcode != null" >
         mm_whcode,
       </if>
+        <if test="mm_whname != null" >
+            mm_whname,
+        </if>
       <if test="mm_price != null" >
         mm_price,
       </if>
@@ -146,6 +150,9 @@
       <if test="mm_whcode != null" >
         #{mm_whcode,jdbcType=VARCHAR},
       </if>
+        <if test="mm_whname != null" >
+            #{mm_whname,jdbcType=VARCHAR},
+        </if>
       <if test="mm_price != null" >
         #{mm_price,jdbcType=DOUBLE},
       </if>
@@ -196,6 +203,9 @@
       <if test="mm_whcode != null" >
         mm_whcode = #{mm_whcode,jdbcType=VARCHAR},
       </if>
+        <if test="mm_whname != null" >
+            mm_whname = #{mm_whname,jdbcType=VARCHAR},
+        </if>
       <if test="mm_price != null" >
         mm_price = #{mm_price,jdbcType=DOUBLE},
       </if>
@@ -234,6 +244,7 @@
       mm_prodcode = #{mm_prodcode,jdbcType=VARCHAR},
       mm_whid = #{mm_whid,jdbcType=INTEGER},
       mm_whcode = #{mm_whcode,jdbcType=VARCHAR},
+      mm_whname = #{mm_whname,jdbcType=VARCHAR},
       mm_price = #{mm_price,jdbcType=DOUBLE},
       mm_oneuseqty = #{mm_oneuseqty,jdbcType=DOUBLE},
       mm_qty = #{mm_qty,jdbcType=DOUBLE},
@@ -254,19 +265,19 @@
   <insert id="batchInsert" parameterType="java.util.List">
       INSERT INTO MAKEMATERIAL (mm_maid, mm_detno,
       mm_prodid, mm_prodcode, mm_whid,
-      mm_whcode, mm_price, mm_oneuseqty
+      mm_whcode, mm_whname, mm_price, mm_oneuseqty,
       mm_qty, mm_amount, mm_repprodcode,
       mm_remark, companyId, updaterId,
-      updateTime,mm_whname)
+      updateTime)
       VALUES
     <foreach collection="list" item="item" index="index" open="" close="" separator=",">
       (
       #{item.mm_maid}, #{item.mm_detno,jdbcType=INTEGER},
       #{item.mm_prodid,jdbcType=INTEGER}, #{item.mm_prodcode,jdbcType=VARCHAR}, #{item.mm_whid,jdbcType=INTEGER},
-      #{item.mm_whcode,jdbcType=VARCHAR}, #{item.mm_price,jdbcType=DOUBLE}, #{item.mm_oneuseqty,jdbcType=DOUBLE},
+      #{item.mm_whcode,jdbcType=VARCHAR}, #{item.mm_whname,jdbcType=VARCHAR}, #{item.mm_price,jdbcType=DOUBLE}, #{item.mm_oneuseqty,jdbcType=DOUBLE},
       #{item.mm_qty,jdbcType=DOUBLE}, #{item.mm_amount,jdbcType=DOUBLE}, #{item.mm_repprodcode,jdbcType=VARCHAR},
       #{item.mm_remark,jdbcType=VARCHAR}, #{item.companyId,jdbcType=INTEGER}, #{item.updaterId,jdbcType=INTEGER},
-      #{item.updateTime,jdbcType=TIMESTAMP},#{item.mm_whname,jdbcType=VARCHAR}
+      #{item.updateTime,jdbcType=TIMESTAMP}
       )
     </foreach>
   </insert>
@@ -292,6 +303,9 @@
         <if test="item.mm_whcode != null" >
           mm_whcode = #{item.mm_whcode,jdbcType=VARCHAR},
         </if>
+          <if test="item.mm_whname != null" >
+              mm_whname = #{item.mm_whname,jdbcType=VARCHAR},
+          </if>
         <if test="item.mm_price != null" >
           mm_price = #{item.mm_price,jdbcType=DOUBLE},
         </if>

+ 8 - 8
applications/storage/storage-server/src/main/resources/mapper/ProdInOutMapper.xml

@@ -19,7 +19,7 @@
     <result column="pi_total" jdbcType="DOUBLE" property="pi_total" />
     <result column="pi_recordmanid" jdbcType="INTEGER" property="pi_recordmanid" />
     <result column="pi_recordman" jdbcType="VARCHAR" property="pi_recordman" />
-    <result column="pi_recorddate" jdbcType="TIMESTAMP" property="pi_recorddate" />
+    <result column="pi_recorddate" jdbcType="TIMESTAMP" property="createTime" />
     <result column="pi_status" jdbcType="VARCHAR" property="pi_status" />
     <result column="pi_statuscode" jdbcType="VARCHAR" property="pi_statuscode" />
     <result column="pi_printstatus" jdbcType="VARCHAR" property="pi_printstatus" />
@@ -120,7 +120,7 @@
       <if test="pi_recordman != null">
         pi_recordman,
       </if>
-      <if test="pi_recorddate != null">
+      <if test="createTime != null">
         pi_recorddate,
       </if>
       <if test="pi_status != null">
@@ -213,8 +213,8 @@
       <if test="pi_recordman != null">
         #{pi_recordman,jdbcType=VARCHAR},
       </if>
-      <if test="pi_recorddate != null">
-        #{pi_recorddate,jdbcType=TIMESTAMP},
+      <if test="createTime != null">
+        #{createTime,jdbcType=TIMESTAMP},
       </if>
       <if test="pi_status != null">
         #{pi_status,jdbcType=VARCHAR},
@@ -309,8 +309,8 @@
       <if test="pi_recordman != null">
         pi_recordman = #{pi_recordman,jdbcType=VARCHAR},
       </if>
-      <if test="pi_recorddate != null">
-        pi_recorddate = #{pi_recorddate,jdbcType=TIMESTAMP},
+      <if test="createTime != null">
+        pi_recorddate = #{createTime,jdbcType=TIMESTAMP},
       </if>
       <if test="pi_status != null">
         pi_status = #{pi_status,jdbcType=VARCHAR},
@@ -373,7 +373,7 @@
       pi_total = #{pi_total,jdbcType=DOUBLE},
       pi_recordmanid = #{pi_recordmanid,jdbcType=INTEGER},
       pi_recordman = #{pi_recordman,jdbcType=VARCHAR},
-      pi_recorddate = #{pi_recorddate,jdbcType=TIMESTAMP},
+      pi_recorddate = #{createTime,jdbcType=TIMESTAMP},
       pi_status = #{pi_status,jdbcType=VARCHAR},
       pi_statuscode = #{pi_statuscode,jdbcType=VARCHAR},
       pi_printstatus = #{pi_printstatus,jdbcType=VARCHAR},
@@ -408,7 +408,7 @@
       pi_total = #{pi_total,jdbcType=DOUBLE},
       pi_recordmanid = #{pi_recordmanid,jdbcType=INTEGER},
       pi_recordman = #{pi_recordman,jdbcType=VARCHAR},
-      pi_recorddate = #{pi_recorddate,jdbcType=TIMESTAMP},
+      pi_recorddate = #{createTime,jdbcType=TIMESTAMP},
       pi_status = #{pi_status,jdbcType=VARCHAR},
       pi_statuscode = #{pi_statuscode,jdbcType=VARCHAR},
       pi_printstatus = #{pi_printstatus,jdbcType=VARCHAR},

+ 10 - 0
base-servers/account/account-api/src/main/java/com/usoftchina/saas/account/cache/AccountCache.java

@@ -4,6 +4,7 @@ import com.usoftchina.saas.account.api.AccountApi;
 import com.usoftchina.saas.account.dto.AccountDTO;
 import com.usoftchina.saas.base.Result;
 import com.usoftchina.saas.cache.RedisHashCache;
+import com.usoftchina.saas.context.BaseContextHolder;
 import com.usoftchina.saas.context.SpringContextHolder;
 import com.usoftchina.saas.exception.BizException;
 import com.usoftchina.saas.utils.JsonUtils;
@@ -31,6 +32,15 @@ public class AccountCache extends RedisHashCache<String, String, String> {
         return new AccountCache(id);
     }
 
+    /**
+     * 当前登录用户的缓存信息
+     *
+     * @return
+     */
+    public static AccountCache current() {
+        return new AccountCache(BaseContextHolder.getUserId());
+    }
+
     @Override
     protected String field() {
         return String.valueOf(id);

+ 10 - 0
base-servers/account/account-api/src/main/java/com/usoftchina/saas/account/cache/ResourceCache.java

@@ -4,6 +4,7 @@ import com.usoftchina.saas.account.api.ResourceApi;
 import com.usoftchina.saas.account.dto.UrlResourceDTO;
 import com.usoftchina.saas.base.Result;
 import com.usoftchina.saas.cache.RedisHashCache;
+import com.usoftchina.saas.context.BaseContextHolder;
 import com.usoftchina.saas.context.SpringContextHolder;
 import com.usoftchina.saas.exception.BizException;
 import com.usoftchina.saas.utils.JsonUtils;
@@ -33,6 +34,15 @@ public class ResourceCache extends RedisHashCache<String, String, String> {
         return new ResourceCache(appId);
     }
 
+    /**
+     * 当前登录应用的资源缓存
+     *
+     * @return
+     */
+    public static ResourceCache current() {
+        return new ResourceCache(BaseContextHolder.getAppId());
+    }
+
     @Override
     protected String field() {
         return appId;

+ 18 - 0
base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/controller/AccountController.java

@@ -100,6 +100,24 @@ public class AccountController {
         return Result.success(getAccountDTO(account));
     }
 
+    /**
+     * 重置当前登录账户的密码
+     *
+     * @param password
+     * @return
+     */
+    @PostMapping(value = "/pwd/reset")
+    public Result resetPad(@RequestParam(value = "password") String password) {
+        Account account = accountService.findByPrimaryKey(BaseContextHolder.getUserId());
+        if (null == account) {
+            return Result.error(ExceptionCode.USER_NOT_EXIST);
+        }
+
+        account.setPassword(accountService.getEncryptedPassword(password, account.getSalt()));
+        accountService.updateByPrimaryKey(account);
+        return Result.success();
+    }
+
     /**
      * 账户 + 账户的全部绑定信息
      *

+ 1 - 1
base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/controller/ResourceController.java

@@ -61,7 +61,7 @@ public class ResourceController {
      */
     @GetMapping("/cache/clear")
     public Result clearCache() {
-        ResourceCache.of(BaseContextHolder.getAppId()).hdel();
+        ResourceCache.current().hdel();
         return Result.success();
     }
 

+ 16 - 0
base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/mapper/AccountMapper.java

@@ -26,6 +26,22 @@ public interface AccountMapper {
      */
     int insertSelective(Account account);
 
+    /**
+     * 按主键更新
+     *
+     * @param account
+     * @return
+     */
+    int updateByPrimaryKey(Account account);
+
+    /**
+     * 按主键更新非空字段
+     *
+     * @param account
+     * @return
+     */
+    int updateByPrimaryKeySelective(Account account);
+
     /**
      * 按ID查找
      *

+ 8 - 0
base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/service/AccountService.java

@@ -18,6 +18,14 @@ public interface AccountService {
      */
     boolean save(Account account);
 
+    /**
+     * 更新
+     *
+     * @param account
+     * @return
+     */
+    boolean updateByPrimaryKey(Account account);
+
     /**
      * 按用户名查找
      *

+ 14 - 0
base-servers/account/account-server/src/main/java/com/usoftchina/saas/account/service/impl/AccountServiceImpl.java

@@ -10,11 +10,13 @@ import com.usoftchina.saas.account.po.Role;
 import com.usoftchina.saas.account.po.RoleResource;
 import com.usoftchina.saas.account.service.AccountService;
 import com.usoftchina.saas.account.service.RoleService;
+import com.usoftchina.saas.context.BaseContextHolder;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.DigestUtils;
 
+import java.util.Date;
 import java.util.List;
 
 /**
@@ -41,9 +43,21 @@ public class AccountServiceImpl implements AccountService {
 
     @Override
     public boolean save(Account account) {
+        account.setCreateTime(new Date());
+        account.setCreatorId(BaseContextHolder.getUserId());
+        account.setUpdateTime(new Date());
+        account.setUpdaterId(BaseContextHolder.getUserId());
+
         return accountMapper.insert(account) > 0;
     }
 
+    @Override
+    public boolean updateByPrimaryKey(Account account) {
+        account.setUpdateTime(new Date());
+        account.setUpdaterId(BaseContextHolder.getUserId());
+        return accountMapper.updateByPrimaryKeySelective(account) > 0;
+    }
+
     @Override
     public Account findByUsername(String username) {
         return accountMapper.selectByUsername(username);

+ 3 - 0
base-servers/account/account-server/src/main/resources/application.yml

@@ -38,6 +38,9 @@ spring:
   redis:
     host: 192.168.253.12
     port: 6379
+  jackson:
+    date-format: yyyy-MM-dd HH:mm:ss
+    time-zone: GMT+8
 eureka:
   instance:
     leaseRenewalIntervalInSeconds: 10

+ 58 - 0
base-servers/account/account-server/src/main/resources/mapper/AccountMapper.xml

@@ -106,6 +106,64 @@
             </if>
         </trim>
     </insert>
+    <update id="updateByPrimaryKey" parameterType="com.usoftchina.saas.account.po.Account">
+        update ac_account set
+        username=#{username,jdbcType=VARCHAR},
+        password=#{password,jdbcType=VARCHAR},
+        salt=#{salt,jdbcType=VARCHAR},
+        realname=#{realname,jdbcType=VARCHAR},
+        email=#{email,jdbcType=VARCHAR},
+        mobile=#{mobile,jdbcType=VARCHAR},
+        type=#{type,jdbcType=INTEGER},
+        enabled=#{enabled,jdbcType=BOOLEAN},
+        creator_id=#{creatorId,jdbcType=BIGINT},
+        create_time=#{createTime,jdbcType=TIMESTAMP},
+        updater_id=#{updaterId,jdbcType=BIGINT},
+        update_time=#{updateTime,jdbcType=TIMESTAMP})
+        where id=#{id,jdbcType=BIGINT}
+    </update>
+    <update id="updateByPrimaryKeySelective" parameterType="com.usoftchina.saas.account.po.Account">
+        update ac_account
+        <set>
+            <if test="username != null">
+                username=#{username,jdbcType=VARCHAR},
+            </if>
+            <if test="password != null">
+                password=#{password,jdbcType=VARCHAR},
+            </if>
+            <if test="salt != null">
+                salt=#{salt,jdbcType=VARCHAR},
+            </if>
+            <if test="realname != null">
+                realname=#{realname,jdbcType=VARCHAR},
+            </if>
+            <if test="email != null">
+                email=#{email,jdbcType=VARCHAR},
+            </if>
+            <if test="mobile != null">
+                mobile=#{mobile,jdbcType=VARCHAR},
+            </if>
+            <if test="type != null">
+                type=#{type,jdbcType=INTEGER},
+            </if>
+            <if test="enabled != null">
+                enabled=#{enabled,jdbcType=BOOLEAN},
+            </if>
+            <if test="creatorId != null">
+                creator_id=#{creatorId,jdbcType=BIGINT},
+            </if>
+            <if test="createTime != null">
+                create_time=#{createTime,jdbcType=TIMESTAMP},
+            </if>
+            <if test="updaterId != null">
+                updater_id=#{updaterId,jdbcType=BIGINT},
+            </if>
+            <if test="updateTime != null">
+                update_time=#{updateTime,jdbcType=TIMESTAMP})
+            </if>
+        </set>
+        where id=#{id,jdbcType=BIGINT}
+    </update>
     <select id="selectByPrimaryKey" parameterType="java.lang.Long" resultMap="BaseResultMap">
         select <include refid="baseColumns"/> from ac_account where id=#{id}
     </select>

+ 9 - 0
base-servers/auth/auth-server/pom.xml

@@ -38,6 +38,10 @@
             <groupId>org.mybatis.spring.boot</groupId>
             <artifactId>mybatis-spring-boot-starter</artifactId>
         </dependency>
+        <dependency>
+            <groupId>com.github.pagehelper</groupId>
+            <artifactId>pagehelper-spring-boot-starter</artifactId>
+        </dependency>
         <!-- api doc -->
         <dependency>
             <groupId>io.springfox</groupId>
@@ -61,6 +65,11 @@
             <groupId>net.logstash.logback</groupId>
             <artifactId>logstash-logback-encoder</artifactId>
         </dependency>
+        <!-- test -->
+        <dependency>
+            <groupId>com.usoftchina.saas</groupId>
+            <artifactId>test-starter</artifactId>
+        </dependency>
     </dependencies>
 
     <build>

+ 2 - 0
base-servers/auth/auth-server/src/main/java/com/usoftchina/saas/auth/AuthApplication.java

@@ -1,5 +1,6 @@
 package com.usoftchina.saas.auth;
 
+import org.mybatis.spring.annotation.MapperScan;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@@ -12,6 +13,7 @@ import org.springframework.cloud.openfeign.EnableFeignClients;
 @SpringBootApplication
 @EnableEurekaClient
 @EnableFeignClients(basePackages = "com.usoftchina.saas.account.api")
+@MapperScan(basePackages = "com.usoftchina.saas.auth.mapper")
 public class AuthApplication {
     public static void main(String[] args) {
         SpringApplication.run(AuthApplication.class, args);

+ 44 - 1
base-servers/auth/auth-server/src/main/java/com/usoftchina/saas/auth/controller/AuthController.java

@@ -1,14 +1,21 @@
 package com.usoftchina.saas.auth.controller;
 
+import com.github.pagehelper.PageInfo;
 import com.usoftchina.saas.account.api.AccountApi;
 import com.usoftchina.saas.account.dto.AccountDTO;
 import com.usoftchina.saas.auth.common.jwt.JwtHelper;
 import com.usoftchina.saas.auth.common.jwt.JwtInfo;
 import com.usoftchina.saas.auth.common.jwt.TokenVO;
 import com.usoftchina.saas.auth.dto.AuthDTO;
+import com.usoftchina.saas.auth.dto.AuthorizeLogDTO;
 import com.usoftchina.saas.auth.dto.TokenDTO;
+import com.usoftchina.saas.auth.po.AuthorizeLog;
+import com.usoftchina.saas.auth.service.AuthorizeCountService;
+import com.usoftchina.saas.auth.service.AuthorizeLogService;
 import com.usoftchina.saas.base.Result;
 import com.usoftchina.saas.exception.ExceptionCode;
+import com.usoftchina.saas.page.PageDefault;
+import com.usoftchina.saas.page.PageRequest;
 import com.usoftchina.saas.utils.BeanMapper;
 import com.usoftchina.saas.utils.CollectionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -40,6 +47,15 @@ public class AuthController {
     @Value("${auth.expire:18000}")
     private int expire;
 
+    @Value("${auth.max-errors:5}")
+    private int maxErrors;
+
+    @Autowired
+    private AuthorizeLogService authorizeLogService;
+
+    @Autowired
+    private AuthorizeCountService authorizeCountService;
+
     /**
      * 登录认证获取token
      *
@@ -48,9 +64,16 @@ public class AuthController {
      * @return
      */
     @PostMapping("/authorize")
-    public Result<AuthDTO> authorize(@RequestParam String username, @RequestParam String password) {
+    public Result<AuthDTO> authorize(HttpServletRequest request, @RequestParam String username, @RequestParam String password) {
+        // 非法操作(登录失败次数过多...)导致被冻结
+        if (authorizeCountService.isFrozen(username)) {
+            return Result.error(ExceptionCode.AUTH_FROZEN);
+        }
+
         Result<AccountDTO> result = accountApi.validByUsernameAndPwd(username, password);
         if (result.isSuccess()) {
+            authorizeCountService.clear(username);
+
             AccountDTO accountDTO = result.getData();
             Long companyId = null;
             if (!CollectionUtils.isEmpty(accountDTO.getCompanies())) {
@@ -62,7 +85,16 @@ public class AuthController {
             JwtInfo info = new JwtInfo(appId, companyId, accountDTO.getId(), accountDTO.getUsername());
             TokenVO tokenVO = JwtHelper.generateToken(info, privateKeyPath, expire);
             TokenDTO tokenDTO = BeanMapper.map(tokenVO, TokenDTO.class);
+            // 登录日志
+            authorizeLogService.save(AuthorizeLog.from(request)
+                    .setAccountId(accountDTO.getId())
+                    .setAppId(appId).build());
             return Result.success(new AuthDTO(tokenDTO, accountDTO));
+        } else {
+            // 失败次数超过最大限制
+            if (authorizeCountService.increaseAndGet(username) > maxErrors) {
+                return Result.error(ExceptionCode.AUTH_MAX_ERRORS);
+            }
         }
         return Result.error(result.getCode(), result.getMessage());
     }
@@ -121,4 +153,15 @@ public class AuthController {
         }
         return false;
     }
+
+    /**
+     * 查询当前用户登录日志
+     *
+     * @param page
+     * @return
+     */
+    @GetMapping("/log")
+    public Result<PageInfo<AuthorizeLogDTO>> getLogs(@PageDefault PageRequest page) {
+        return Result.success(authorizeLogService.findByPage(page));
+    }
 }

+ 47 - 0
base-servers/auth/auth-server/src/main/java/com/usoftchina/saas/auth/dto/AuthorizeLogDTO.java

@@ -0,0 +1,47 @@
+package com.usoftchina.saas.auth.dto;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * @author yingp
+ * @date 2018/11/7
+ */
+public class AuthorizeLogDTO implements Serializable{
+    private String clientIp;
+    private String userAgent;
+    private Date loginTime;
+
+    public String getClientIp() {
+        return clientIp;
+    }
+
+    public void setClientIp(String clientIp) {
+        this.clientIp = clientIp;
+    }
+
+    public String getUserAgent() {
+        return userAgent;
+    }
+
+    public void setUserAgent(String userAgent) {
+        this.userAgent = userAgent;
+    }
+
+    public Date getLoginTime() {
+        return loginTime;
+    }
+
+    public void setLoginTime(Date loginTime) {
+        this.loginTime = loginTime;
+    }
+
+    @Override
+    public String toString() {
+        return "AuthorizeLogDTO{" +
+                "clientIp='" + clientIp + '\'' +
+                ", userAgent='" + userAgent + '\'' +
+                ", loginTime=" + loginTime +
+                '}';
+    }
+}

+ 29 - 0
base-servers/auth/auth-server/src/main/java/com/usoftchina/saas/auth/mapper/AuthorizeLogMapper.java

@@ -0,0 +1,29 @@
+package com.usoftchina.saas.auth.mapper;
+
+import com.usoftchina.saas.auth.po.AuthorizeLog;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+/**
+ * @author yingp
+ * @date 2018/11/6
+ */
+public interface AuthorizeLogMapper {
+    /**
+     * 保存
+     *
+     * @param authorizeLog
+     * @return
+     */
+    int insert(AuthorizeLog authorizeLog);
+
+    /**
+     * 查找个人日志
+     *
+     * @param appId
+     * @param accountId
+     * @return
+     */
+    List<AuthorizeLog> selectByAppIdAndAccountId(@Param("appId") String appId, @Param("accountId") Long accountId);
+}

+ 117 - 0
base-servers/auth/auth-server/src/main/java/com/usoftchina/saas/auth/po/AuthorizeLog.java

@@ -0,0 +1,117 @@
+package com.usoftchina.saas.auth.po;
+
+import javax.servlet.http.HttpServletRequest;
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * @author yingp
+ * @date 2018/11/6
+ */
+public class AuthorizeLog implements Serializable{
+    private Long id;
+    private Long accountId;
+    private String clientIp;
+    private String userAgent;
+    private Date loginTime;
+    private String appId;
+
+    public AuthorizeLog() {
+    }
+
+    public AuthorizeLog(Long accountId, String clientIp, String userAgent, String appId) {
+        this.accountId = accountId;
+        this.clientIp = clientIp;
+        this.userAgent = userAgent;
+        this.loginTime = new Date();
+        this.appId = appId;
+    }
+
+    public Long getId() {
+        return id;
+    }
+
+    public void setId(Long id) {
+        this.id = id;
+    }
+
+    public Long getAccountId() {
+        return accountId;
+    }
+
+    public void setAccountId(Long accountId) {
+        this.accountId = accountId;
+    }
+
+    public String getClientIp() {
+        return clientIp;
+    }
+
+    public void setClientIp(String clientIp) {
+        this.clientIp = clientIp;
+    }
+
+    public String getUserAgent() {
+        return userAgent;
+    }
+
+    public void setUserAgent(String userAgent) {
+        this.userAgent = userAgent;
+    }
+
+    public Date getLoginTime() {
+        return loginTime;
+    }
+
+    public void setLoginTime(Date loginTime) {
+        this.loginTime = loginTime;
+    }
+
+    public String getAppId() {
+        return appId;
+    }
+
+    public void setAppId(String appId) {
+        this.appId = appId;
+    }
+
+    public static Builder from(HttpServletRequest request) {
+        return new Builder(request);
+    }
+
+    public static class Builder {
+        private Long accountId;
+        private String clientIp;
+        private String userAgent;
+        private String appId;
+
+        public Builder(HttpServletRequest request) {
+            this.clientIp = request.getRemoteAddr();
+            this.userAgent = request.getHeader("User-Agent");
+        }
+
+        public Builder setAccountId(Long accountId) {
+            this.accountId = accountId;
+            return this;
+        }
+
+        public Builder setClientIp(String clientIp) {
+            this.clientIp = clientIp;
+            return this;
+        }
+
+        public Builder setUserAgent(String userAgent) {
+            this.userAgent = userAgent;
+            return this;
+        }
+
+        public Builder setAppId(String appId) {
+            this.appId = appId;
+            return this;
+        }
+
+        public AuthorizeLog build() {
+            return new AuthorizeLog(accountId, clientIp, userAgent, appId);
+        }
+    }
+}

+ 68 - 0
base-servers/auth/auth-server/src/main/java/com/usoftchina/saas/auth/service/AuthorizeCountService.java

@@ -0,0 +1,68 @@
+package com.usoftchina.saas.auth.service;
+
+import com.usoftchina.saas.cache.CacheKeyHelper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Service;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 登录失败记录
+ *
+ * @author yingp
+ * @date 2018/11/6
+ */
+@Service
+public class AuthorizeCountService {
+
+    @Autowired
+    private StringRedisTemplate redisTemplate;
+
+    @Value("${auth.max-errors:5}")
+    private int maxErrors;
+
+    /**
+     * 账户锁定时间
+     */
+    @Value("${auth.error-lock-time:30}")
+    private int lockTime;
+
+    /**
+     * 记录一次
+     *
+     * @param username
+     * @return
+     */
+    public Long increaseAndGet(String username) {
+        String key = generateKey(username);
+        Long value = redisTemplate.opsForValue().increment(key, 1);
+        redisTemplate.expire(key, lockTime, TimeUnit.MINUTES);
+        return value;
+    }
+
+    private String generateKey(String username) {
+        return CacheKeyHelper.generatePrivateKey("auth", "authorize", username);
+    }
+
+    /**
+     * 清零
+     *
+     * @param username
+     */
+    public void clear(String username) {
+        redisTemplate.delete(generateKey(username));
+    }
+
+    /**
+     * 账户是否冻结
+     *
+     * @param username
+     * @return
+     */
+    public boolean isFrozen(String username) {
+        Object value = redisTemplate.opsForValue().get(generateKey(username));
+        return null != value && Integer.parseInt(value.toString()) > maxErrors;
+    }
+}

+ 28 - 0
base-servers/auth/auth-server/src/main/java/com/usoftchina/saas/auth/service/AuthorizeLogService.java

@@ -0,0 +1,28 @@
+package com.usoftchina.saas.auth.service;
+
+import com.github.pagehelper.PageInfo;
+import com.usoftchina.saas.auth.dto.AuthorizeLogDTO;
+import com.usoftchina.saas.auth.po.AuthorizeLog;
+import com.usoftchina.saas.page.PageRequest;
+
+/**
+ * @author yingp
+ * @date 2018/11/6
+ */
+public interface AuthorizeLogService {
+    /**
+     * 保存
+     *
+     * @param authorizeLog
+     * @return
+     */
+    boolean save(AuthorizeLog authorizeLog);
+
+    /**
+     * 分页查询
+     *
+     * @param page
+     * @return
+     */
+    PageInfo<AuthorizeLogDTO> findByPage(PageRequest page);
+}

+ 42 - 0
base-servers/auth/auth-server/src/main/java/com/usoftchina/saas/auth/service/impl/AuthorizeLogServiceImpl.java

@@ -0,0 +1,42 @@
+package com.usoftchina.saas.auth.service.impl;
+
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
+import com.usoftchina.saas.auth.dto.AuthorizeLogDTO;
+import com.usoftchina.saas.auth.mapper.AuthorizeLogMapper;
+import com.usoftchina.saas.auth.po.AuthorizeLog;
+import com.usoftchina.saas.auth.service.AuthorizeLogService;
+import com.usoftchina.saas.context.BaseContextHolder;
+import com.usoftchina.saas.page.PageRequest;
+import com.usoftchina.saas.utils.BeanMapper;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.scheduling.annotation.Async;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+/**
+ * @author yingp
+ * @date 2018/11/6
+ */
+@Service
+public class AuthorizeLogServiceImpl implements AuthorizeLogService{
+
+    @Autowired
+    private AuthorizeLogMapper authorizeLogMapper;
+
+    @Override
+    @Async
+    public boolean save(AuthorizeLog authorizeLog) {
+        return authorizeLogMapper.insert(authorizeLog) > 0;
+    }
+
+    @Override
+    public PageInfo<AuthorizeLogDTO> findByPage(PageRequest page) {
+        PageHelper.startPage(page.getNumber(), page.getSize());
+        List<AuthorizeLog> logs = authorizeLogMapper.selectByAppIdAndAccountId(
+                BaseContextHolder.getAppId(), BaseContextHolder.getCompanyId()
+        );
+        return new PageInfo<>(BeanMapper.mapList(logs, AuthorizeLogDTO.class));
+    }
+}

+ 3 - 0
base-servers/auth/auth-server/src/main/resources/application.yml

@@ -38,6 +38,9 @@ spring:
   redis:
     host: 192.168.253.12
     port: 6379
+  jackson:
+    date-format: yyyy-MM-dd HH:mm:ss
+    time-zone: GMT+8
 eureka:
   instance:
     leaseRenewalIntervalInSeconds: 10

+ 21 - 0
base-servers/auth/auth-server/src/main/resources/mapper/AuthorizeLogMapper.xml

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="com.usoftchina.saas.auth.mapper.AuthorizeLogMapper">
+    <insert id="insert">
+        insert into au_authorize_log(app_id,account_id,client_ip,user_agent,login_time)
+        values (#{appId,jdbcType=VARCHAR}, #{accountId,jdbcType=BIGINT}, #{clientIp,jdbcType=VARCHAR},
+        #{userAgent,jdbcType=VARCHAR}, #{loginTime,jdbcType=TIMESTAMP})
+    </insert>
+    <resultMap id="BaseResultMap" type="com.usoftchina.saas.auth.po.AuthorizeLog">
+        <id column="id" property="id" jdbcType="BIGINT" />
+        <result column="account_id" property="accountId" jdbcType="BIGINT" />
+        <result column="client_ip" property="clientIp" jdbcType="VARCHAR" />
+        <result column="user_agent" property="userAgent" jdbcType="VARCHAR" />
+        <result column="login_time" property="loginTime" jdbcType="TIMESTAMP" />
+        <result column="app_id" property="appId" jdbcType="VARCHAR" />
+    </resultMap>
+    <select id="selectByAppIdAndAccountId" resultMap="BaseResultMap">
+        select * from au_authorize_log where app_id=#{appId} and account_id=#{accountId}
+        order by login_time desc
+    </select>
+</mapper>

+ 48 - 0
base-servers/auth/auth-server/src/test/java/com/usoftchina/saas/auth/controller/AuthControllerTest.java

@@ -0,0 +1,48 @@
+package com.usoftchina.saas.auth.controller;
+
+import com.github.pagehelper.PageInfo;
+import com.usoftchina.saas.auth.dto.AuthDTO;
+import com.usoftchina.saas.auth.dto.AuthorizeLogDTO;
+import com.usoftchina.saas.base.Result;
+import com.usoftchina.saas.test.BaseControllerTest;
+import com.usoftchina.saas.utils.JsonUtils;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MvcResult;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class AuthControllerTest extends BaseControllerTest {
+
+    private static String TOKEN;
+
+    @Test
+    public void testA_authorize() throws Exception {
+        MvcResult mvcResult = mockMvc.perform(post("/authorize")
+                .param("username", "18888888888")
+                .param("password", "select111***"))
+                .andExpect(isSuccess())
+                .andReturn();
+        Result<AuthDTO> result = result(mvcResult, AuthDTO.class);
+        System.out.println(result.getData());
+        TOKEN = result.getData().getToken().getToken();
+    }
+
+    @Test
+    public void testB_getLogs() throws Exception {
+        MvcResult mvcResult = mockMvc.perform(get("/log")
+                .param("size", "5")
+                .header("Authorization", TOKEN))
+                .andExpect(isSuccess())
+                .andReturn();
+        Result<PageInfo<AuthorizeLogDTO>> result = result(mvcResult,
+                JsonUtils.getJavaType(PageInfo.class, AuthorizeLogDTO.class));
+        System.out.println(result.getData());
+    }
+
+}

+ 2 - 2
base-servers/gateway-server/src/main/java/com/usoftchina/saas/gateway/config/AuthFilter.java

@@ -50,7 +50,7 @@ public class AuthFilter implements GlobalFilter, Ordered {
                 BaseContextHolder.setUserId(jwt.getUserId());
                 BaseContextHolder.setCompanyId(jwt.getCompanyId());
                 BaseContextHolder.setToken(token);
-                AccountDTO accountDTO = AccountCache.of(jwt.getUserId()).getAccount();
+                AccountDTO accountDTO = AccountCache.current().getAccount();
                 if (null == accountDTO) {
                     throw new BizException(ExceptionCode.USER_NOT_EXIST);
                 }
@@ -73,7 +73,7 @@ public class AuthFilter implements GlobalFilter, Ordered {
     private void checkPermission(ServerHttpRequest request, JwtInfo jwt, AccountDTO accountDTO) {
         if (!accountDTO.isAdmin(jwt.getCompanyId())) {
             // 非管理账户,需要鉴权
-            List<UrlResourceDTO> resources = ResourceCache.of(jwt.getAppId()).getUrlResources();
+            List<UrlResourceDTO> resources = ResourceCache.current().getUrlResources();
             if (!CollectionUtils.isEmpty(resources)) {
                 // 本次请求相关的资源
                 List<UrlResourceDTO> permissions = resources.parallelStream().filter(resource -> {

+ 3 - 0
base-servers/gateway-server/src/main/resources/application.yml

@@ -107,6 +107,9 @@ spring:
   redis:
     host: 192.168.253.12
     port: 6379
+  jackson:
+    date-format: yyyy-MM-dd HH:mm:ss
+    time-zone: GMT+8
 server:
   port: 8560
   tomcat:

+ 3 - 0
base-servers/ui-server/src/main/resources/application.yml

@@ -25,6 +25,9 @@ spring:
   data:
     mongodb:
       uri: mongodb://192.168.253.12:27017/saas_ui
+  jackson:
+    date-format: yyyy-MM-dd HH:mm:ss
+    time-zone: GMT+8
 server:
   port: 8620
   tomcat:

+ 6 - 0
framework/core/pom.xml

@@ -34,6 +34,12 @@
             <scope>provided</scope>
             <optional>true</optional>
         </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-web</artifactId>
+            <scope>provided</scope>
+            <optional>true</optional>
+        </dependency>
         <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-lang3</artifactId>

+ 15 - 2
framework/core/src/main/java/com/usoftchina/saas/base/Result.java

@@ -1,5 +1,6 @@
 package com.usoftchina.saas.base;
 
+import com.fasterxml.jackson.databind.JavaType;
 import com.usoftchina.saas.exception.BaseException;
 import com.usoftchina.saas.exception.BaseExceptionCode;
 import com.usoftchina.saas.utils.JsonUtils;
@@ -138,7 +139,19 @@ public class Result<T> implements Serializable {
      * @param <T>
      * @return
      */
-    public static <T> Result<T> fromJsonString(String jsonString, Class<T> targetCls) {
-        return JsonUtils.fromJsonString(jsonString, Result.class, targetCls);
+    public static <T> Result<T> fromJsonString(String jsonString, Class<T> targetClass) {
+        return JsonUtils.fromJsonString(jsonString, Result.class, targetClass);
+    }
+
+    /**
+     * json字符串转换Result对象
+     *
+     * @param jsonString
+     * @param targetType
+     * @param <T>
+     * @return
+     */
+    public static <T> Result<T> fromJsonString(String jsonString, JavaType targetType) {
+        return JsonUtils.fromJsonString(jsonString, JsonUtils.getJavaType(Result.class, targetType));
     }
 }

+ 2 - 11
framework/core/src/main/java/com/usoftchina/saas/cache/BaseRedisCache.java

@@ -1,11 +1,9 @@
 package com.usoftchina.saas.cache;
 
-import com.usoftchina.saas.context.SpringContextHolder;
 import org.springframework.data.redis.core.RedisTemplate;
 
 import java.util.Objects;
 import java.util.Optional;
-import java.util.StringJoiner;
 import java.util.concurrent.TimeUnit;
 import java.util.function.Supplier;
 
@@ -108,11 +106,7 @@ public abstract class BaseRedisCache<K, V> implements Cache<V> {
      * @return
      */
     protected String generatePrivateKey(CharSequence... values) {
-        return new StringJoiner(":")
-                .add(SpringContextHolder.getActiveProfile())
-                .add(SpringContextHolder.getApplicationName())
-                .add(String.join(":", values))
-                .toString();
+        return CacheKeyHelper.generatePrivateKey(values);
     }
 
     /**
@@ -123,9 +117,6 @@ public abstract class BaseRedisCache<K, V> implements Cache<V> {
      * @return
      */
     protected String generatePublicKey(CharSequence... values) {
-        return new StringJoiner(":")
-                .add(SpringContextHolder.getActiveProfile())
-                .add(String.join(":", values))
-                .toString();
+        return CacheKeyHelper.generatePublicKey(values);
     }
 }

+ 40 - 0
framework/core/src/main/java/com/usoftchina/saas/cache/CacheKeyHelper.java

@@ -0,0 +1,40 @@
+package com.usoftchina.saas.cache;
+
+import com.usoftchina.saas.context.SpringContextHolder;
+
+import java.util.StringJoiner;
+
+/**
+ * @author yingp
+ * @date 2018/11/6
+ */
+public class CacheKeyHelper {
+    /**
+     * 产生key (每个应用会不一样)
+     * 规则:[env profile]:[application name]:[business key]
+     *
+     * @param values
+     * @return
+     */
+    public static String generatePrivateKey(CharSequence... values) {
+        return new StringJoiner(":")
+                .add(SpringContextHolder.getActiveProfile())
+                .add(SpringContextHolder.getApplicationName())
+                .add(String.join(":", values))
+                .toString();
+    }
+
+    /**
+     * 产生key (所有应用一样)
+     * 规则:[env profile]:[business key]
+     *
+     * @param values
+     * @return
+     */
+    public static String generatePublicKey(CharSequence... values) {
+        return new StringJoiner(":")
+                .add(SpringContextHolder.getActiveProfile())
+                .add(String.join(":", values))
+                .toString();
+    }
+}

+ 5 - 0
framework/core/src/main/java/com/usoftchina/saas/exception/ExceptionCode.java

@@ -9,6 +9,7 @@ public enum ExceptionCode implements BaseExceptionCode {
     SYSTEM_BUSY(-1, "系统繁忙,请稍候再试"),
     SYSTEM_TIMEOUT(-2, "系统超时,请稍候再试"),
 
+    INVALID_DEFAULT_PAGE(10001, "无效默认分页参数"),
     // jwt token 相关 start
     // 过期
     JWT_TOKEN_EXPIRED(40001, "token超时,请检查 token 的有效期"),
@@ -21,6 +22,10 @@ public enum ExceptionCode implements BaseExceptionCode {
     JWT_APPID_SECRET_INVALID(40006, "获取 access_token 时 AppSecret 错误,或者 AppId 无效!"),
     JWT_APPID_ENABLED(40007, "AppId 已经被禁用!请联系管理员"),
 
+    // authorize相关
+    AUTH_MAX_ERRORS(43001, "超过登录次数限制,账户已被冻结,请30分钟后再尝试"),
+    AUTH_FROZEN(43002, "账户已被冻结,请30分钟后再尝试"),
+
     // 账户管理相关
     COMPANY_NAME_EXIST(52000, "公司名称已注册"),
     COMPANY_CODE_EXIST(52001, "公司商业登记证号已注册"),

+ 23 - 0
framework/core/src/main/java/com/usoftchina/saas/page/PageDefault.java

@@ -0,0 +1,23 @@
+package com.usoftchina.saas.page;
+
+import java.lang.annotation.*;
+
+/**
+ * 默认分页参数
+ *
+ * @author yingp
+ * @date 2018/11/7
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+public @interface PageDefault {
+    /**
+     * 页码
+     */
+    int number() default 1;
+    /**
+     * 每页条数
+     */
+    int size() default 20;
+}

+ 12 - 0
framework/core/src/main/java/com/usoftchina/saas/page/PageRequest.java

@@ -16,6 +16,18 @@ public class PageRequest implements Serializable{
      */
     private int size;
 
+    public PageRequest() {
+    }
+
+    public PageRequest(int number, int size) {
+        this.number = number;
+        this.size = size;
+    }
+
+    public static PageRequest of(int number, int size) {
+        return new PageRequest(number, size);
+    }
+
     public int getNumber() {
         return number;
     }

+ 96 - 0
framework/core/src/main/java/com/usoftchina/saas/page/PageRequestArgumentResolver.java

@@ -0,0 +1,96 @@
+package com.usoftchina.saas.page;
+
+import com.usoftchina.saas.exception.BizException;
+import com.usoftchina.saas.exception.ExceptionCode;
+import org.springframework.core.MethodParameter;
+import org.springframework.lang.Nullable;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.support.WebDataBinderFactory;
+import org.springframework.web.context.request.NativeWebRequest;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.method.support.ModelAndViewContainer;
+
+import java.util.Optional;
+
+/**
+ * 分页参数处理器,设置分页默认参数
+ *
+ * @author yingp
+ * @date 2018/11/7
+ */
+public class PageRequestArgumentResolver implements HandlerMethodArgumentResolver {
+
+    static final PageRequest DEFAULT_PAGE_REQUEST = PageRequest.of(1, 20);
+
+    private PageRequest fallbackPageable = DEFAULT_PAGE_REQUEST;
+
+    private static final int DEFAULT_MAX_PAGE_SIZE = 2000;
+
+    private int maxPageSize = DEFAULT_MAX_PAGE_SIZE;
+
+    @Nullable
+    @Override
+    public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception {
+        PageRequest defaultOrFallback = getDefaultFromAnnotationOrFallback(parameter);
+
+        Optional<Integer> pageNumber = parseAndApplyBoundaries(webRequest.getParameter("number"),
+                Integer.MAX_VALUE);
+        Optional<Integer> pageSize = parseAndApplyBoundaries(webRequest.getParameter("size"),
+                maxPageSize);
+
+        int pn = pageNumber.orElseGet(defaultOrFallback::getNumber);
+        int ps = pageSize.orElseGet(defaultOrFallback::getSize);
+
+        return PageRequest.of(pn, ps);
+    }
+
+    @Override
+    public boolean supportsParameter(MethodParameter parameter) {
+        return parameter.hasParameterAnnotation(PageDefault.class);
+    }
+
+    private PageRequest getDefaultFromAnnotationOrFallback(MethodParameter parameter) {
+        PageDefault pageDefault = parameter.getParameterAnnotation(PageDefault.class);
+        if (pageDefault != null) {
+            return getDefaultPageRequestFrom(parameter, pageDefault);
+        }
+        return fallbackPageable;
+    }
+
+    private static PageRequest getDefaultPageRequestFrom(MethodParameter parameter, PageDefault pageDefault) {
+        Integer defaultPageNumber = pageDefault.number();
+        Integer defaultPageSize = pageDefault.size();
+
+        if (defaultPageNumber < 1 || defaultPageSize < 1) {
+            throw new BizException(ExceptionCode.INVALID_DEFAULT_PAGE);
+        }
+
+        return new PageRequest(defaultPageNumber, defaultPageSize);
+    }
+
+    /**
+     * Tries to parse the given {@link String} into an integer and applies the given boundaries. Will return 0 if the
+     * {@link String} cannot be parsed.
+     *
+     * @param parameter the parameter value.
+     * @param upper the upper bound to be applied.
+     * @return
+     */
+    private Optional<Integer> parseAndApplyBoundaries(@Nullable String parameter, int upper) {
+
+        if (!StringUtils.hasText(parameter)) {
+            return Optional.empty();
+        }
+
+        try {
+            int parsed = Integer.parseInt(parameter);
+            return Optional.of(parsed < 0 ? 0 : parsed > upper ? upper : parsed);
+        } catch (NumberFormatException e) {
+            return Optional.of(0);
+        }
+    }
+
+    public void setMaxPageSize(int maxPageSize) {
+        this.maxPageSize = maxPageSize;
+    }
+}

+ 13 - 13
framework/core/src/main/java/com/usoftchina/saas/utils/JsonUtils.java

@@ -4,7 +4,9 @@ import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.JavaType;
 import com.fasterxml.jackson.databind.ObjectMapper;
 
+import java.text.SimpleDateFormat;
 import java.util.List;
+import java.util.TimeZone;
 
 /**
  * @author yingp
@@ -12,7 +14,13 @@ import java.util.List;
  */
 public class JsonUtils {
 
-    private static ObjectMapper mapper = new ObjectMapper();
+    private static ObjectMapper mapper;
+
+    static {
+        mapper = new ObjectMapper()
+                .setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))
+                .setTimeZone(TimeZone.getTimeZone("GMT+8"));
+    }
 
     public static String toJsonString(Object object) {
         try {
@@ -33,22 +41,14 @@ public class JsonUtils {
         }
     }
 
-    public static <T> T fromJsonString(String json, TypeReference valueTypeRef) {
-        if (StringUtils.isEmpty(json)) {
-            return null;
-        }
-        try {
-            return mapper.readValue(json, valueTypeRef);
-        } catch (Exception e) {
-            e.printStackTrace();
-            return null;
-        }
-    }
-
     public static JavaType getJavaType(Class<?> targetClass, Class<?>... elementClasses) {
         return mapper.getTypeFactory().constructParametricType(targetClass, elementClasses);
     }
 
+    public static JavaType getJavaType(Class<?> targetClass, JavaType... parameterTypes) {
+        return mapper.getTypeFactory().constructParametricType(targetClass, parameterTypes);
+    }
+
     public static <T> T fromJsonString(String json, Class<?> targetClass, Class<?>... elementClasses) {
         if (StringUtils.isEmpty(json)) {
             return null;

+ 29 - 0
framework/server-starter/src/main/java/com/usoftchina/saas/server/web/DefaultWebMvcConfig.java

@@ -0,0 +1,29 @@
+package com.usoftchina.saas.server.web;
+
+import com.usoftchina.saas.page.PageRequestArgumentResolver;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import java.util.List;
+
+/**
+ * @author yingp
+ * @date 2018/11/7
+ */
+@Configuration
+public class DefaultWebMvcConfig implements WebMvcConfigurer{
+
+    @Bean
+    @ConditionalOnMissingBean
+    public PageRequestArgumentResolver pageRequestArgumentResolver() {
+        return new PageRequestArgumentResolver();
+    }
+
+    @Override
+    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
+        resolvers.add(pageRequestArgumentResolver());
+    }
+}

+ 17 - 2
framework/test-starter/src/main/java/com.usoftchina.saas.test/BaseControllerTest.java

@@ -1,5 +1,6 @@
 package com.usoftchina.saas.test;
 
+import com.fasterxml.jackson.databind.JavaType;
 import com.usoftchina.saas.base.Result;
 import com.usoftchina.saas.utils.JsonUtils;
 import org.junit.Before;
@@ -110,9 +111,23 @@ public abstract class BaseControllerTest {
      * @return
      * @throws Exception
      */
-    public static <T> Result<T> result(MvcResult mvcResult, Class<T> targetCls) throws Exception {
+    public static <T> Result<T> result(MvcResult mvcResult, Class<T> targetClass) throws Exception {
         String content = mvcResult.getResponse().getContentAsString();
-        return Result.fromJsonString(content, targetCls);
+        return Result.fromJsonString(content, targetClass);
+    }
+
+    /**
+     * Result转换
+     *
+     * @param mvcResult
+     * @param targetType
+     * @param <T>
+     * @return
+     * @throws Exception
+     */
+    public static <T> Result<T> result(MvcResult mvcResult, JavaType targetType) throws Exception {
+        String content = mvcResult.getResponse().getContentAsString();
+        return Result.fromJsonString(content, targetType);
     }
 
 }

+ 15 - 0
frontend/saas-portal-web/.gitignore

@@ -0,0 +1,15 @@
+.DS_Store
+node_modules/
+dist/
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+package-lock.json
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln

+ 5 - 0
frontend/saas-portal-web/Dockerfile

@@ -0,0 +1,5 @@
+FROM hub.c.163.com/library/nginx
+MAINTAINER USOFTCHINA <yingp@usoftchina.com>
+RUN rm /etc/nginx/conf.d/default.conf
+ADD runtime/nginx/default.conf /etc/nginx/conf.d/
+COPY src/ /usr/share/nginx/html/

+ 15 - 0
frontend/saas-portal-web/README.md

@@ -0,0 +1,15 @@
+## Build Setup
+
+```bash
+# Install dependencies
+yarn
+
+# 或者
+npm install --registry=https://registry.npm.taobao.org
+
+# Serve at 127.0.0.1
+npm run dev
+
+# Build for production with minification
+npm run build
+```

+ 5 - 0
frontend/saas-portal-web/config/dev.env.js

@@ -0,0 +1,5 @@
+'use strict'
+module.exports = {
+  NODE_ENV: '"development"',
+  BASE_API: '"http://192.168.0.181:8560"',
+}

+ 5 - 0
frontend/saas-portal-web/config/prod.env.js

@@ -0,0 +1,5 @@
+'use strict'
+module.exports = {
+  NODE_ENV: '"production"',
+  BASE_API: '"http://192.168.0.181:8560"',
+}

+ 21 - 0
frontend/saas-portal-web/package.json

@@ -0,0 +1,21 @@
+{
+  "name": "saas-portal-web",
+  "version": "1.0.0",
+  "description": "saas portal",
+  "main": "index.js",
+  "scripts": {
+    "dev": "webpack-dev-server --inline --progress --config ./webpack.conf.js",
+    "start": "npm run dev",
+    "build": "webpack --mode production --config ./webpack.conf.js"
+  },
+  "keywords": [
+    "saas"
+  ],
+  "author": "yingp@usoftchina.com",
+  "license": "ISC",
+  "devDependencies": {
+    "webpack": "^4.25.1",
+    "webpack-cli": "^3.1.2",
+    "webpack-dev-server": "^3.1.10"
+  }
+}

+ 20 - 0
frontend/saas-portal-web/runtime/nginx/default.conf

@@ -0,0 +1,20 @@
+server {
+    listen       80;
+    server_name  localhost;
+
+    charset utf-8;
+
+    location / {
+        root   /usr/share/nginx/html;
+        index  index.html index.htm;
+    }
+
+    #error_page  404              /404.html;
+
+    # redirect server error pages to the static page /50x.html
+    #
+    error_page   500 502 503 504  /50x.html;
+    location = /50x.html {
+        root   html;
+    }
+}

+ 3296 - 0
frontend/saas-portal-web/src/css/animate.css

@@ -0,0 +1,3296 @@
+/*!
+Animate.css - http://daneden.me/animate
+Licensed under the MIT license - http://opensource.org/licenses/MIT
+
+Copyright (c) 2013 Daniel Eden
+*/
+
+
+.animated {
+  -webkit-animation-duration: 1s;
+          animation-duration: 1s;
+  -webkit-animation-fill-mode: both;
+          animation-fill-mode: both;
+}
+
+.animated.infinite {
+  -webkit-animation-iteration-count: infinite;
+          animation-iteration-count: infinite;
+}
+
+.animated.hinge {
+  -webkit-animation-duration: 2s;
+          animation-duration: 2s;
+}
+
+@-webkit-keyframes bounce {
+  0%, 20%, 50%, 80%, 100% {
+    -webkit-transform: translateY(0);
+    transform: translateY(0);
+  }
+
+  40% {
+    -webkit-transform: translateY(-30px);
+    transform: translateY(-30px);
+  }
+
+  60% {
+    -webkit-transform: translateY(-15px);
+    transform: translateY(-15px);
+  }
+}
+
+@keyframes bounce {
+  0%, 20%, 50%, 80%, 100% {
+    -webkit-transform: translateY(0);
+    -ms-transform: translateY(0);
+    transform: translateY(0);
+  }
+
+  40% {
+    -webkit-transform: translateY(-30px);
+    -ms-transform: translateY(-30px);
+    transform: translateY(-30px);
+  }
+
+  60% {
+    -webkit-transform: translateY(-15px);
+    -ms-transform: translateY(-15px);
+    transform: translateY(-15px);
+  }
+}
+
+.bounce {
+  -webkit-animation-name: bounce;
+  animation-name: bounce;
+}
+
+@-webkit-keyframes flash {
+  0%, 50%, 100% {
+    opacity: 1;
+  }
+
+  25%, 75% {
+    opacity: 0;
+  }
+}
+
+@keyframes flash {
+  0%, 50%, 100% {
+    opacity: 1;
+  }
+
+  25%, 75% {
+    opacity: 0;
+  }
+}
+
+.flash {
+  -webkit-animation-name: flash;
+  animation-name: flash;
+}
+
+/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */
+
+@-webkit-keyframes pulse {
+  0% {
+    -webkit-transform: scale(1);
+    transform: scale(1);
+  }
+
+  50% {
+    -webkit-transform: scale(1.1);
+    transform: scale(1.1);
+  }
+
+  100% {
+    -webkit-transform: scale(1);
+    transform: scale(1);
+  }
+}
+
+@keyframes pulse {
+  0% {
+    -webkit-transform: scale(1);
+    -ms-transform: scale(1);
+    transform: scale(1);
+  }
+
+  50% {
+    -webkit-transform: scale(1.1);
+    -ms-transform: scale(1.1);
+    transform: scale(1.1);
+  }
+
+  100% {
+    -webkit-transform: scale(1);
+    -ms-transform: scale(1);
+    transform: scale(1);
+  }
+}
+
+.pulse {
+  -webkit-animation-name: pulse;
+  animation-name: pulse;
+}
+
+@-webkit-keyframes rubberBand {
+  0% {
+    -webkit-transform: scale(1);
+    transform: scale(1);
+  }
+
+  30% {
+    -webkit-transform: scaleX(1.25) scaleY(0.75);
+    transform: scaleX(1.25) scaleY(0.75);
+  }
+
+  40% {
+    -webkit-transform: scaleX(0.75) scaleY(1.25);
+    transform: scaleX(0.75) scaleY(1.25);
+  }
+
+  60% {
+    -webkit-transform: scaleX(1.15) scaleY(0.85);
+    transform: scaleX(1.15) scaleY(0.85);
+  }
+
+  100% {
+    -webkit-transform: scale(1);
+    transform: scale(1);
+  }
+}
+
+@keyframes rubberBand {
+  0% {
+    -webkit-transform: scale(1);
+    -ms-transform: scale(1);
+    transform: scale(1);
+  }
+
+  30% {
+    -webkit-transform: scaleX(1.25) scaleY(0.75);
+    -ms-transform: scaleX(1.25) scaleY(0.75);
+    transform: scaleX(1.25) scaleY(0.75);
+  }
+
+  40% {
+    -webkit-transform: scaleX(0.75) scaleY(1.25);
+    -ms-transform: scaleX(0.75) scaleY(1.25);
+    transform: scaleX(0.75) scaleY(1.25);
+  }
+
+  60% {
+    -webkit-transform: scaleX(1.15) scaleY(0.85);
+    -ms-transform: scaleX(1.15) scaleY(0.85);
+    transform: scaleX(1.15) scaleY(0.85);
+  }
+
+  100% {
+    -webkit-transform: scale(1);
+    -ms-transform: scale(1);
+    transform: scale(1);
+  }
+}
+
+.rubberBand {
+  -webkit-animation-name: rubberBand;
+  animation-name: rubberBand;
+}
+
+@-webkit-keyframes shake {
+  0%, 100% {
+    -webkit-transform: translateX(0);
+    transform: translateX(0);
+  }
+
+  10%, 30%, 50%, 70%, 90% {
+    -webkit-transform: translateX(-10px);
+    transform: translateX(-10px);
+  }
+
+  20%, 40%, 60%, 80% {
+    -webkit-transform: translateX(10px);
+    transform: translateX(10px);
+  }
+}
+
+@keyframes shake {
+  0%, 100% {
+    -webkit-transform: translateX(0);
+    -ms-transform: translateX(0);
+    transform: translateX(0);
+  }
+
+  10%, 30%, 50%, 70%, 90% {
+    -webkit-transform: translateX(-10px);
+    -ms-transform: translateX(-10px);
+    transform: translateX(-10px);
+  }
+
+  20%, 40%, 60%, 80% {
+    -webkit-transform: translateX(10px);
+    -ms-transform: translateX(10px);
+    transform: translateX(10px);
+  }
+}
+
+.shake {
+  -webkit-animation-name: shake;
+  animation-name: shake;
+}
+
+@-webkit-keyframes swing {
+  20% {
+    -webkit-transform: rotate(15deg);
+    transform: rotate(15deg);
+  }
+
+  40% {
+    -webkit-transform: rotate(-10deg);
+    transform: rotate(-10deg);
+  }
+
+  60% {
+    -webkit-transform: rotate(5deg);
+    transform: rotate(5deg);
+  }
+
+  80% {
+    -webkit-transform: rotate(-5deg);
+    transform: rotate(-5deg);
+  }
+
+  100% {
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+}
+
+@keyframes swing {
+  20% {
+    -webkit-transform: rotate(15deg);
+    -ms-transform: rotate(15deg);
+    transform: rotate(15deg);
+  }
+
+  40% {
+    -webkit-transform: rotate(-10deg);
+    -ms-transform: rotate(-10deg);
+    transform: rotate(-10deg);
+  }
+
+  60% {
+    -webkit-transform: rotate(5deg);
+    -ms-transform: rotate(5deg);
+    transform: rotate(5deg);
+  }
+
+  80% {
+    -webkit-transform: rotate(-5deg);
+    -ms-transform: rotate(-5deg);
+    transform: rotate(-5deg);
+  }
+
+  100% {
+    -webkit-transform: rotate(0deg);
+    -ms-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+}
+
+.swing {
+  -webkit-transform-origin: top center;
+  -ms-transform-origin: top center;
+  transform-origin: top center;
+  -webkit-animation-name: swing;
+  animation-name: swing;
+}
+
+@-webkit-keyframes tada {
+  0% {
+    -webkit-transform: scale(1);
+    transform: scale(1);
+  }
+
+  10%, 20% {
+    -webkit-transform: scale(0.9) rotate(-3deg);
+    transform: scale(0.9) rotate(-3deg);
+  }
+
+  30%, 50%, 70%, 90% {
+    -webkit-transform: scale(1.1) rotate(3deg);
+    transform: scale(1.1) rotate(3deg);
+  }
+
+  40%, 60%, 80% {
+    -webkit-transform: scale(1.1) rotate(-3deg);
+    transform: scale(1.1) rotate(-3deg);
+  }
+
+  100% {
+    -webkit-transform: scale(1) rotate(0);
+    transform: scale(1) rotate(0);
+  }
+}
+
+@keyframes tada {
+  0% {
+    -webkit-transform: scale(1);
+    -ms-transform: scale(1);
+    transform: scale(1);
+  }
+
+  10%, 20% {
+    -webkit-transform: scale(0.9) rotate(-3deg);
+    -ms-transform: scale(0.9) rotate(-3deg);
+    transform: scale(0.9) rotate(-3deg);
+  }
+
+  30%, 50%, 70%, 90% {
+    -webkit-transform: scale(1.1) rotate(3deg);
+    -ms-transform: scale(1.1) rotate(3deg);
+    transform: scale(1.1) rotate(3deg);
+  }
+
+  40%, 60%, 80% {
+    -webkit-transform: scale(1.1) rotate(-3deg);
+    -ms-transform: scale(1.1) rotate(-3deg);
+    transform: scale(1.1) rotate(-3deg);
+  }
+
+  100% {
+    -webkit-transform: scale(1) rotate(0);
+    -ms-transform: scale(1) rotate(0);
+    transform: scale(1) rotate(0);
+  }
+}
+
+.tada {
+  -webkit-animation-name: tada;
+  animation-name: tada;
+}
+
+/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */
+
+@-webkit-keyframes wobble {
+  0% {
+    -webkit-transform: translateX(0%);
+    transform: translateX(0%);
+  }
+
+  15% {
+    -webkit-transform: translateX(-25%) rotate(-5deg);
+    transform: translateX(-25%) rotate(-5deg);
+  }
+
+  30% {
+    -webkit-transform: translateX(20%) rotate(3deg);
+    transform: translateX(20%) rotate(3deg);
+  }
+
+  45% {
+    -webkit-transform: translateX(-15%) rotate(-3deg);
+    transform: translateX(-15%) rotate(-3deg);
+  }
+
+  60% {
+    -webkit-transform: translateX(10%) rotate(2deg);
+    transform: translateX(10%) rotate(2deg);
+  }
+
+  75% {
+    -webkit-transform: translateX(-5%) rotate(-1deg);
+    transform: translateX(-5%) rotate(-1deg);
+  }
+
+  100% {
+    -webkit-transform: translateX(0%);
+    transform: translateX(0%);
+  }
+}
+
+@keyframes wobble {
+  0% {
+    -webkit-transform: translateX(0%);
+    -ms-transform: translateX(0%);
+    transform: translateX(0%);
+  }
+
+  15% {
+    -webkit-transform: translateX(-25%) rotate(-5deg);
+    -ms-transform: translateX(-25%) rotate(-5deg);
+    transform: translateX(-25%) rotate(-5deg);
+  }
+
+  30% {
+    -webkit-transform: translateX(20%) rotate(3deg);
+    -ms-transform: translateX(20%) rotate(3deg);
+    transform: translateX(20%) rotate(3deg);
+  }
+
+  45% {
+    -webkit-transform: translateX(-15%) rotate(-3deg);
+    -ms-transform: translateX(-15%) rotate(-3deg);
+    transform: translateX(-15%) rotate(-3deg);
+  }
+
+  60% {
+    -webkit-transform: translateX(10%) rotate(2deg);
+    -ms-transform: translateX(10%) rotate(2deg);
+    transform: translateX(10%) rotate(2deg);
+  }
+
+  75% {
+    -webkit-transform: translateX(-5%) rotate(-1deg);
+    -ms-transform: translateX(-5%) rotate(-1deg);
+    transform: translateX(-5%) rotate(-1deg);
+  }
+
+  100% {
+    -webkit-transform: translateX(0%);
+    -ms-transform: translateX(0%);
+    transform: translateX(0%);
+  }
+}
+
+.wobble {
+  -webkit-animation-name: wobble;
+  animation-name: wobble;
+}
+
+@-webkit-keyframes bounceIn {
+  0% {
+    opacity: 0;
+    -webkit-transform: scale(.3);
+    transform: scale(.3);
+  }
+
+  50% {
+    opacity: 1;
+    -webkit-transform: scale(1.05);
+    transform: scale(1.05);
+  }
+
+  70% {
+    -webkit-transform: scale(.9);
+    transform: scale(.9);
+  }
+
+  100% {
+    opacity: 1;
+    -webkit-transform: scale(1);
+    transform: scale(1);
+  }
+}
+
+@keyframes bounceIn {
+  0% {
+    opacity: 0;
+    -webkit-transform: scale(.3);
+    -ms-transform: scale(.3);
+    transform: scale(.3);
+  }
+
+  50% {
+    opacity: 1;
+    -webkit-transform: scale(1.05);
+    -ms-transform: scale(1.05);
+    transform: scale(1.05);
+  }
+
+  70% {
+    -webkit-transform: scale(.9);
+    -ms-transform: scale(.9);
+    transform: scale(.9);
+  }
+
+  100% {
+    opacity: 1;
+    -webkit-transform: scale(1);
+    -ms-transform: scale(1);
+    transform: scale(1);
+  }
+}
+
+.bounceIn {
+  -webkit-animation-name: bounceIn;
+  animation-name: bounceIn;
+}
+
+@-webkit-keyframes bounceInDown {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateY(-2000px);
+    transform: translateY(-2000px);
+  }
+
+  60% {
+    opacity: 1;
+    -webkit-transform: translateY(30px);
+    transform: translateY(30px);
+  }
+
+  80% {
+    -webkit-transform: translateY(-10px);
+    transform: translateY(-10px);
+  }
+
+  100% {
+    -webkit-transform: translateY(0);
+    transform: translateY(0);
+  }
+}
+
+@keyframes bounceInDown {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateY(-2000px);
+    -ms-transform: translateY(-2000px);
+    transform: translateY(-2000px);
+  }
+
+  60% {
+    opacity: 1;
+    -webkit-transform: translateY(30px);
+    -ms-transform: translateY(30px);
+    transform: translateY(30px);
+  }
+
+  80% {
+    -webkit-transform: translateY(-10px);
+    -ms-transform: translateY(-10px);
+    transform: translateY(-10px);
+  }
+
+  100% {
+    -webkit-transform: translateY(0);
+    -ms-transform: translateY(0);
+    transform: translateY(0);
+  }
+}
+
+.bounceInDown {
+  -webkit-animation-name: bounceInDown;
+  animation-name: bounceInDown;
+}
+
+@-webkit-keyframes bounceInLeft {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateX(-2000px);
+    transform: translateX(-2000px);
+  }
+
+  60% {
+    opacity: 1;
+    -webkit-transform: translateX(30px);
+    transform: translateX(30px);
+  }
+
+  80% {
+    -webkit-transform: translateX(-10px);
+    transform: translateX(-10px);
+  }
+
+  100% {
+    -webkit-transform: translateX(0);
+    transform: translateX(0);
+  }
+}
+
+@keyframes bounceInLeft {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateX(-2000px);
+    -ms-transform: translateX(-2000px);
+    transform: translateX(-2000px);
+  }
+
+  60% {
+    opacity: 1;
+    -webkit-transform: translateX(30px);
+    -ms-transform: translateX(30px);
+    transform: translateX(30px);
+  }
+
+  80% {
+    -webkit-transform: translateX(-10px);
+    -ms-transform: translateX(-10px);
+    transform: translateX(-10px);
+  }
+
+  100% {
+    -webkit-transform: translateX(0);
+    -ms-transform: translateX(0);
+    transform: translateX(0);
+  }
+}
+
+.bounceInLeft {
+  -webkit-animation-name: bounceInLeft;
+  animation-name: bounceInLeft;
+}
+
+@-webkit-keyframes bounceInRight {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateX(2000px);
+    transform: translateX(2000px);
+  }
+
+  60% {
+    opacity: 1;
+    -webkit-transform: translateX(-30px);
+    transform: translateX(-30px);
+  }
+
+  80% {
+    -webkit-transform: translateX(10px);
+    transform: translateX(10px);
+  }
+
+  100% {
+    -webkit-transform: translateX(0);
+    transform: translateX(0);
+  }
+}
+
+@keyframes bounceInRight {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateX(2000px);
+    -ms-transform: translateX(2000px);
+    transform: translateX(2000px);
+  }
+
+  60% {
+    opacity: 1;
+    -webkit-transform: translateX(-30px);
+    -ms-transform: translateX(-30px);
+    transform: translateX(-30px);
+  }
+
+  80% {
+    -webkit-transform: translateX(10px);
+    -ms-transform: translateX(10px);
+    transform: translateX(10px);
+  }
+
+  100% {
+    -webkit-transform: translateX(0);
+    -ms-transform: translateX(0);
+    transform: translateX(0);
+  }
+}
+
+.bounceInRight {
+  -webkit-animation-name: bounceInRight;
+  animation-name: bounceInRight;
+}
+
+@-webkit-keyframes bounceInUp {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateY(2000px);
+    transform: translateY(2000px);
+  }
+
+  60% {
+    opacity: 1;
+    -webkit-transform: translateY(-30px);
+    transform: translateY(-30px);
+  }
+
+  80% {
+    -webkit-transform: translateY(10px);
+    transform: translateY(10px);
+  }
+
+  100% {
+    -webkit-transform: translateY(0);
+    transform: translateY(0);
+  }
+}
+
+@keyframes bounceInUp {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateY(2000px);
+    -ms-transform: translateY(2000px);
+    transform: translateY(2000px);
+  }
+
+  60% {
+    opacity: 1;
+    -webkit-transform: translateY(-30px);
+    -ms-transform: translateY(-30px);
+    transform: translateY(-30px);
+  }
+
+  80% {
+    -webkit-transform: translateY(10px);
+    -ms-transform: translateY(10px);
+    transform: translateY(10px);
+  }
+
+  100% {
+    -webkit-transform: translateY(0);
+    -ms-transform: translateY(0);
+    transform: translateY(0);
+  }
+}
+
+.bounceInUp {
+  -webkit-animation-name: bounceInUp;
+  animation-name: bounceInUp;
+}
+
+@-webkit-keyframes bounceOut {
+  0% {
+    -webkit-transform: scale(1);
+    transform: scale(1);
+  }
+
+  25% {
+    -webkit-transform: scale(.95);
+    transform: scale(.95);
+  }
+
+  50% {
+    opacity: 1;
+    -webkit-transform: scale(1.1);
+    transform: scale(1.1);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: scale(.3);
+    transform: scale(.3);
+  }
+}
+
+@keyframes bounceOut {
+  0% {
+    -webkit-transform: scale(1);
+    -ms-transform: scale(1);
+    transform: scale(1);
+  }
+
+  25% {
+    -webkit-transform: scale(.95);
+    -ms-transform: scale(.95);
+    transform: scale(.95);
+  }
+
+  50% {
+    opacity: 1;
+    -webkit-transform: scale(1.1);
+    -ms-transform: scale(1.1);
+    transform: scale(1.1);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: scale(.3);
+    -ms-transform: scale(.3);
+    transform: scale(.3);
+  }
+}
+
+.bounceOut {
+  -webkit-animation-name: bounceOut;
+  animation-name: bounceOut;
+}
+
+@-webkit-keyframes bounceOutDown {
+  0% {
+    -webkit-transform: translateY(0);
+    transform: translateY(0);
+  }
+
+  20% {
+    opacity: 1;
+    -webkit-transform: translateY(-20px);
+    transform: translateY(-20px);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateY(2000px);
+    transform: translateY(2000px);
+  }
+}
+
+@keyframes bounceOutDown {
+  0% {
+    -webkit-transform: translateY(0);
+    -ms-transform: translateY(0);
+    transform: translateY(0);
+  }
+
+  20% {
+    opacity: 1;
+    -webkit-transform: translateY(-20px);
+    -ms-transform: translateY(-20px);
+    transform: translateY(-20px);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateY(2000px);
+    -ms-transform: translateY(2000px);
+    transform: translateY(2000px);
+  }
+}
+
+.bounceOutDown {
+  -webkit-animation-name: bounceOutDown;
+  animation-name: bounceOutDown;
+}
+
+@-webkit-keyframes bounceOutLeft {
+  0% {
+    -webkit-transform: translateX(0);
+    transform: translateX(0);
+  }
+
+  20% {
+    opacity: 1;
+    -webkit-transform: translateX(20px);
+    transform: translateX(20px);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateX(-2000px);
+    transform: translateX(-2000px);
+  }
+}
+
+@keyframes bounceOutLeft {
+  0% {
+    -webkit-transform: translateX(0);
+    -ms-transform: translateX(0);
+    transform: translateX(0);
+  }
+
+  20% {
+    opacity: 1;
+    -webkit-transform: translateX(20px);
+    -ms-transform: translateX(20px);
+    transform: translateX(20px);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateX(-2000px);
+    -ms-transform: translateX(-2000px);
+    transform: translateX(-2000px);
+  }
+}
+
+.bounceOutLeft {
+  -webkit-animation-name: bounceOutLeft;
+  animation-name: bounceOutLeft;
+}
+
+@-webkit-keyframes bounceOutRight {
+  0% {
+    -webkit-transform: translateX(0);
+    transform: translateX(0);
+  }
+
+  20% {
+    opacity: 1;
+    -webkit-transform: translateX(-20px);
+    transform: translateX(-20px);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateX(2000px);
+    transform: translateX(2000px);
+  }
+}
+
+@keyframes bounceOutRight {
+  0% {
+    -webkit-transform: translateX(0);
+    -ms-transform: translateX(0);
+    transform: translateX(0);
+  }
+
+  20% {
+    opacity: 1;
+    -webkit-transform: translateX(-20px);
+    -ms-transform: translateX(-20px);
+    transform: translateX(-20px);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateX(2000px);
+    -ms-transform: translateX(2000px);
+    transform: translateX(2000px);
+  }
+}
+
+.bounceOutRight {
+  -webkit-animation-name: bounceOutRight;
+  animation-name: bounceOutRight;
+}
+
+@-webkit-keyframes bounceOutUp {
+  0% {
+    -webkit-transform: translateY(0);
+    transform: translateY(0);
+  }
+
+  20% {
+    opacity: 1;
+    -webkit-transform: translateY(20px);
+    transform: translateY(20px);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateY(-2000px);
+    transform: translateY(-2000px);
+  }
+}
+
+@keyframes bounceOutUp {
+  0% {
+    -webkit-transform: translateY(0);
+    -ms-transform: translateY(0);
+    transform: translateY(0);
+  }
+
+  20% {
+    opacity: 1;
+    -webkit-transform: translateY(20px);
+    -ms-transform: translateY(20px);
+    transform: translateY(20px);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateY(-2000px);
+    -ms-transform: translateY(-2000px);
+    transform: translateY(-2000px);
+  }
+}
+
+.bounceOutUp {
+  -webkit-animation-name: bounceOutUp;
+  animation-name: bounceOutUp;
+}
+
+@-webkit-keyframes fadeIn {
+  0% {
+    opacity: 0;
+  }
+
+  100% {
+    opacity: 1;
+  }
+}
+
+@keyframes fadeIn {
+  0% {
+    opacity: 0;
+  }
+
+  100% {
+    opacity: 1;
+  }
+}
+
+.fadeIn {
+  -webkit-animation-name: fadeIn;
+  animation-name: fadeIn;
+}
+
+@-webkit-keyframes fadeInDown {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateY(-20px);
+    transform: translateY(-20px);
+  }
+
+  100% {
+    opacity: 1;
+    -webkit-transform: translateY(0);
+    transform: translateY(0);
+  }
+}
+
+@keyframes fadeInDown {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateY(-20px);
+    -ms-transform: translateY(-20px);
+    transform: translateY(-20px);
+  }
+
+  100% {
+    opacity: 1;
+    -webkit-transform: translateY(0);
+    -ms-transform: translateY(0);
+    transform: translateY(0);
+  }
+}
+
+.fadeInDown {
+  -webkit-animation-name: fadeInDown;
+  animation-name: fadeInDown;
+}
+
+@-webkit-keyframes fadeInDownBig {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateY(-2000px);
+    transform: translateY(-2000px);
+  }
+
+  100% {
+    opacity: 1;
+    -webkit-transform: translateY(0);
+    transform: translateY(0);
+  }
+}
+
+@keyframes fadeInDownBig {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateY(-2000px);
+    -ms-transform: translateY(-2000px);
+    transform: translateY(-2000px);
+  }
+
+  100% {
+    opacity: 1;
+    -webkit-transform: translateY(0);
+    -ms-transform: translateY(0);
+    transform: translateY(0);
+  }
+}
+
+.fadeInDownBig {
+  -webkit-animation-name: fadeInDownBig;
+  animation-name: fadeInDownBig;
+}
+
+@-webkit-keyframes fadeInLeft {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateX(-20px);
+    transform: translateX(-20px);
+  }
+
+  100% {
+    opacity: 1;
+    -webkit-transform: translateX(0);
+    transform: translateX(0);
+  }
+}
+
+@keyframes fadeInLeft {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateX(-20px);
+    -ms-transform: translateX(-20px);
+    transform: translateX(-20px);
+  }
+
+  100% {
+    opacity: 1;
+    -webkit-transform: translateX(0);
+    -ms-transform: translateX(0);
+    transform: translateX(0);
+  }
+}
+
+.fadeInLeft {
+  -webkit-animation-name: fadeInLeft;
+  animation-name: fadeInLeft;
+}
+
+@-webkit-keyframes fadeInLeftBig {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateX(-2000px);
+    transform: translateX(-2000px);
+  }
+
+  100% {
+    opacity: 1;
+    -webkit-transform: translateX(0);
+    transform: translateX(0);
+  }
+}
+
+@keyframes fadeInLeftBig {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateX(-2000px);
+    -ms-transform: translateX(-2000px);
+    transform: translateX(-2000px);
+  }
+
+  100% {
+    opacity: 1;
+    -webkit-transform: translateX(0);
+    -ms-transform: translateX(0);
+    transform: translateX(0);
+  }
+}
+
+.fadeInLeftBig {
+  -webkit-animation-name: fadeInLeftBig;
+  animation-name: fadeInLeftBig;
+}
+
+@-webkit-keyframes fadeInRight {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateX(20px);
+    transform: translateX(20px);
+  }
+
+  100% {
+    opacity: 1;
+    -webkit-transform: translateX(0);
+    transform: translateX(0);
+  }
+}
+
+@keyframes fadeInRight {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateX(20px);
+    -ms-transform: translateX(20px);
+    transform: translateX(20px);
+  }
+
+  100% {
+    opacity: 1;
+    -webkit-transform: translateX(0);
+    -ms-transform: translateX(0);
+    transform: translateX(0);
+  }
+}
+
+.fadeInRight {
+  -webkit-animation-name: fadeInRight;
+  animation-name: fadeInRight;
+}
+
+@-webkit-keyframes fadeInRightBig {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateX(2000px);
+    transform: translateX(2000px);
+  }
+
+  100% {
+    opacity: 1;
+    -webkit-transform: translateX(0);
+    transform: translateX(0);
+  }
+}
+
+@keyframes fadeInRightBig {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateX(2000px);
+    -ms-transform: translateX(2000px);
+    transform: translateX(2000px);
+  }
+
+  100% {
+    opacity: 1;
+    -webkit-transform: translateX(0);
+    -ms-transform: translateX(0);
+    transform: translateX(0);
+  }
+}
+
+.fadeInRightBig {
+  -webkit-animation-name: fadeInRightBig;
+  animation-name: fadeInRightBig;
+}
+
+@-webkit-keyframes fadeInUp {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateY(20px);
+    transform: translateY(20px);
+  }
+
+  100% {
+    opacity: 1;
+    -webkit-transform: translateY(0);
+    transform: translateY(0);
+  }
+}
+
+@keyframes fadeInUp {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateY(20px);
+    -ms-transform: translateY(20px);
+    transform: translateY(20px);
+  }
+
+  100% {
+    opacity: 1;
+    -webkit-transform: translateY(0);
+    -ms-transform: translateY(0);
+    transform: translateY(0);
+  }
+}
+
+.fadeInUp {
+  -webkit-animation-name: fadeInUp;
+  animation-name: fadeInUp;
+}
+
+@-webkit-keyframes fadeInUpBig {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateY(2000px);
+    transform: translateY(2000px);
+  }
+
+  100% {
+    opacity: 1;
+    -webkit-transform: translateY(0);
+    transform: translateY(0);
+  }
+}
+
+@keyframes fadeInUpBig {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateY(2000px);
+    -ms-transform: translateY(2000px);
+    transform: translateY(2000px);
+  }
+
+  100% {
+    opacity: 1;
+    -webkit-transform: translateY(0);
+    -ms-transform: translateY(0);
+    transform: translateY(0);
+  }
+}
+
+.fadeInUpBig {
+  -webkit-animation-name: fadeInUpBig;
+  animation-name: fadeInUpBig;
+}
+
+@-webkit-keyframes fadeOut {
+  0% {
+    opacity: 1;
+  }
+
+  100% {
+    opacity: 0;
+  }
+}
+
+@keyframes fadeOut {
+  0% {
+    opacity: 1;
+  }
+
+  100% {
+    opacity: 0;
+  }
+}
+
+.fadeOut {
+  -webkit-animation-name: fadeOut;
+  animation-name: fadeOut;
+}
+
+@-webkit-keyframes fadeOutDown {
+  0% {
+    opacity: 1;
+    -webkit-transform: translateY(0);
+    transform: translateY(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateY(20px);
+    transform: translateY(20px);
+  }
+}
+
+@keyframes fadeOutDown {
+  0% {
+    opacity: 1;
+    -webkit-transform: translateY(0);
+    -ms-transform: translateY(0);
+    transform: translateY(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateY(20px);
+    -ms-transform: translateY(20px);
+    transform: translateY(20px);
+  }
+}
+
+.fadeOutDown {
+  -webkit-animation-name: fadeOutDown;
+  animation-name: fadeOutDown;
+}
+
+@-webkit-keyframes fadeOutDownBig {
+  0% {
+    opacity: 1;
+    -webkit-transform: translateY(0);
+    transform: translateY(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateY(2000px);
+    transform: translateY(2000px);
+  }
+}
+
+@keyframes fadeOutDownBig {
+  0% {
+    opacity: 1;
+    -webkit-transform: translateY(0);
+    -ms-transform: translateY(0);
+    transform: translateY(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateY(2000px);
+    -ms-transform: translateY(2000px);
+    transform: translateY(2000px);
+  }
+}
+
+.fadeOutDownBig {
+  -webkit-animation-name: fadeOutDownBig;
+  animation-name: fadeOutDownBig;
+}
+
+@-webkit-keyframes fadeOutLeft {
+  0% {
+    opacity: 1;
+    -webkit-transform: translateX(0);
+    transform: translateX(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateX(-20px);
+    transform: translateX(-20px);
+  }
+}
+
+@keyframes fadeOutLeft {
+  0% {
+    opacity: 1;
+    -webkit-transform: translateX(0);
+    -ms-transform: translateX(0);
+    transform: translateX(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateX(-20px);
+    -ms-transform: translateX(-20px);
+    transform: translateX(-20px);
+  }
+}
+
+.fadeOutLeft {
+  -webkit-animation-name: fadeOutLeft;
+  animation-name: fadeOutLeft;
+}
+
+@-webkit-keyframes fadeOutLeftBig {
+  0% {
+    opacity: 1;
+    -webkit-transform: translateX(0);
+    transform: translateX(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateX(-2000px);
+    transform: translateX(-2000px);
+  }
+}
+
+@keyframes fadeOutLeftBig {
+  0% {
+    opacity: 1;
+    -webkit-transform: translateX(0);
+    -ms-transform: translateX(0);
+    transform: translateX(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateX(-2000px);
+    -ms-transform: translateX(-2000px);
+    transform: translateX(-2000px);
+  }
+}
+
+.fadeOutLeftBig {
+  -webkit-animation-name: fadeOutLeftBig;
+  animation-name: fadeOutLeftBig;
+}
+
+@-webkit-keyframes fadeOutRight {
+  0% {
+    opacity: 1;
+    -webkit-transform: translateX(0);
+    transform: translateX(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateX(20px);
+    transform: translateX(20px);
+  }
+}
+
+@keyframes fadeOutRight {
+  0% {
+    opacity: 1;
+    -webkit-transform: translateX(0);
+    -ms-transform: translateX(0);
+    transform: translateX(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateX(20px);
+    -ms-transform: translateX(20px);
+    transform: translateX(20px);
+  }
+}
+
+.fadeOutRight {
+  -webkit-animation-name: fadeOutRight;
+  animation-name: fadeOutRight;
+}
+
+@-webkit-keyframes fadeOutRightBig {
+  0% {
+    opacity: 1;
+    -webkit-transform: translateX(0);
+    transform: translateX(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateX(2000px);
+    transform: translateX(2000px);
+  }
+}
+
+@keyframes fadeOutRightBig {
+  0% {
+    opacity: 1;
+    -webkit-transform: translateX(0);
+    -ms-transform: translateX(0);
+    transform: translateX(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateX(2000px);
+    -ms-transform: translateX(2000px);
+    transform: translateX(2000px);
+  }
+}
+
+.fadeOutRightBig {
+  -webkit-animation-name: fadeOutRightBig;
+  animation-name: fadeOutRightBig;
+}
+
+@-webkit-keyframes fadeOutUp {
+  0% {
+    opacity: 1;
+    -webkit-transform: translateY(0);
+    transform: translateY(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateY(-20px);
+    transform: translateY(-20px);
+  }
+}
+
+@keyframes fadeOutUp {
+  0% {
+    opacity: 1;
+    -webkit-transform: translateY(0);
+    -ms-transform: translateY(0);
+    transform: translateY(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateY(-20px);
+    -ms-transform: translateY(-20px);
+    transform: translateY(-20px);
+  }
+}
+
+.fadeOutUp {
+  -webkit-animation-name: fadeOutUp;
+  animation-name: fadeOutUp;
+}
+
+@-webkit-keyframes fadeOutUpBig {
+  0% {
+    opacity: 1;
+    -webkit-transform: translateY(0);
+    transform: translateY(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateY(-2000px);
+    transform: translateY(-2000px);
+  }
+}
+
+@keyframes fadeOutUpBig {
+  0% {
+    opacity: 1;
+    -webkit-transform: translateY(0);
+    -ms-transform: translateY(0);
+    transform: translateY(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateY(-2000px);
+    -ms-transform: translateY(-2000px);
+    transform: translateY(-2000px);
+  }
+}
+
+.fadeOutUpBig {
+  -webkit-animation-name: fadeOutUpBig;
+  animation-name: fadeOutUpBig;
+}
+
+@-webkit-keyframes flip {
+  0% {
+    -webkit-transform: perspective(400px) translateZ(0) rotateY(-360deg) scale(1);
+    transform: perspective(400px) translateZ(0) rotateY(-360deg) scale(1);
+    -webkit-animation-timing-function: ease-out;
+    animation-timing-function: ease-out;
+  }
+
+  40% {
+    -webkit-transform: perspective(400px) translateZ(150px) rotateY(-190deg) scale(1);
+    transform: perspective(400px) translateZ(150px) rotateY(-190deg) scale(1);
+    -webkit-animation-timing-function: ease-out;
+    animation-timing-function: ease-out;
+  }
+
+  50% {
+    -webkit-transform: perspective(400px) translateZ(150px) rotateY(-170deg) scale(1);
+    transform: perspective(400px) translateZ(150px) rotateY(-170deg) scale(1);
+    -webkit-animation-timing-function: ease-in;
+    animation-timing-function: ease-in;
+  }
+
+  80% {
+    -webkit-transform: perspective(400px) translateZ(0) rotateY(0deg) scale(.95);
+    transform: perspective(400px) translateZ(0) rotateY(0deg) scale(.95);
+    -webkit-animation-timing-function: ease-in;
+    animation-timing-function: ease-in;
+  }
+
+  100% {
+    -webkit-transform: perspective(400px) translateZ(0) rotateY(0deg) scale(1);
+    transform: perspective(400px) translateZ(0) rotateY(0deg) scale(1);
+    -webkit-animation-timing-function: ease-in;
+    animation-timing-function: ease-in;
+  }
+}
+
+@keyframes flip {
+  0% {
+    -webkit-transform: perspective(400px) translateZ(0) rotateY(-360deg) scale(1);
+    -ms-transform: perspective(400px) translateZ(0) rotateY(-360deg) scale(1);
+    transform: perspective(400px) translateZ(0) rotateY(-360deg) scale(1);
+    -webkit-animation-timing-function: ease-out;
+    animation-timing-function: ease-out;
+  }
+
+  40% {
+    -webkit-transform: perspective(400px) translateZ(150px) rotateY(-190deg) scale(1);
+    -ms-transform: perspective(400px) translateZ(150px) rotateY(-190deg) scale(1);
+    transform: perspective(400px) translateZ(150px) rotateY(-190deg) scale(1);
+    -webkit-animation-timing-function: ease-out;
+    animation-timing-function: ease-out;
+  }
+
+  50% {
+    -webkit-transform: perspective(400px) translateZ(150px) rotateY(-170deg) scale(1);
+    -ms-transform: perspective(400px) translateZ(150px) rotateY(-170deg) scale(1);
+    transform: perspective(400px) translateZ(150px) rotateY(-170deg) scale(1);
+    -webkit-animation-timing-function: ease-in;
+    animation-timing-function: ease-in;
+  }
+
+  80% {
+    -webkit-transform: perspective(400px) translateZ(0) rotateY(0deg) scale(.95);
+    -ms-transform: perspective(400px) translateZ(0) rotateY(0deg) scale(.95);
+    transform: perspective(400px) translateZ(0) rotateY(0deg) scale(.95);
+    -webkit-animation-timing-function: ease-in;
+    animation-timing-function: ease-in;
+  }
+
+  100% {
+    -webkit-transform: perspective(400px) translateZ(0) rotateY(0deg) scale(1);
+    -ms-transform: perspective(400px) translateZ(0) rotateY(0deg) scale(1);
+    transform: perspective(400px) translateZ(0) rotateY(0deg) scale(1);
+    -webkit-animation-timing-function: ease-in;
+    animation-timing-function: ease-in;
+  }
+}
+
+.animated.flip {
+  -webkit-backface-visibility: visible;
+  -ms-backface-visibility: visible;
+  backface-visibility: visible;
+  -webkit-animation-name: flip;
+  animation-name: flip;
+}
+
+@-webkit-keyframes flipInX {
+  0% {
+    -webkit-transform: perspective(400px) rotateX(90deg);
+    transform: perspective(400px) rotateX(90deg);
+    opacity: 0;
+  }
+
+  40% {
+    -webkit-transform: perspective(400px) rotateX(-10deg);
+    transform: perspective(400px) rotateX(-10deg);
+  }
+
+  70% {
+    -webkit-transform: perspective(400px) rotateX(10deg);
+    transform: perspective(400px) rotateX(10deg);
+  }
+
+  100% {
+    -webkit-transform: perspective(400px) rotateX(0deg);
+    transform: perspective(400px) rotateX(0deg);
+    opacity: 1;
+  }
+}
+
+@keyframes flipInX {
+  0% {
+    -webkit-transform: perspective(400px) rotateX(90deg);
+    -ms-transform: perspective(400px) rotateX(90deg);
+    transform: perspective(400px) rotateX(90deg);
+    opacity: 0;
+  }
+
+  40% {
+    -webkit-transform: perspective(400px) rotateX(-10deg);
+    -ms-transform: perspective(400px) rotateX(-10deg);
+    transform: perspective(400px) rotateX(-10deg);
+  }
+
+  70% {
+    -webkit-transform: perspective(400px) rotateX(10deg);
+    -ms-transform: perspective(400px) rotateX(10deg);
+    transform: perspective(400px) rotateX(10deg);
+  }
+
+  100% {
+    -webkit-transform: perspective(400px) rotateX(0deg);
+    -ms-transform: perspective(400px) rotateX(0deg);
+    transform: perspective(400px) rotateX(0deg);
+    opacity: 1;
+  }
+}
+
+.flipInX {
+  -webkit-backface-visibility: visible !important;
+  -ms-backface-visibility: visible !important;
+  backface-visibility: visible !important;
+  -webkit-animation-name: flipInX;
+  animation-name: flipInX;
+}
+
+@-webkit-keyframes flipInY {
+  0% {
+    -webkit-transform: perspective(400px) rotateY(90deg);
+    transform: perspective(400px) rotateY(90deg);
+    opacity: 0;
+  }
+
+  40% {
+    -webkit-transform: perspective(400px) rotateY(-10deg);
+    transform: perspective(400px) rotateY(-10deg);
+  }
+
+  70% {
+    -webkit-transform: perspective(400px) rotateY(10deg);
+    transform: perspective(400px) rotateY(10deg);
+  }
+
+  100% {
+    -webkit-transform: perspective(400px) rotateY(0deg);
+    transform: perspective(400px) rotateY(0deg);
+    opacity: 1;
+  }
+}
+
+@keyframes flipInY {
+  0% {
+    -webkit-transform: perspective(400px) rotateY(90deg);
+    -ms-transform: perspective(400px) rotateY(90deg);
+    transform: perspective(400px) rotateY(90deg);
+    opacity: 0;
+  }
+
+  40% {
+    -webkit-transform: perspective(400px) rotateY(-10deg);
+    -ms-transform: perspective(400px) rotateY(-10deg);
+    transform: perspective(400px) rotateY(-10deg);
+  }
+
+  70% {
+    -webkit-transform: perspective(400px) rotateY(10deg);
+    -ms-transform: perspective(400px) rotateY(10deg);
+    transform: perspective(400px) rotateY(10deg);
+  }
+
+  100% {
+    -webkit-transform: perspective(400px) rotateY(0deg);
+    -ms-transform: perspective(400px) rotateY(0deg);
+    transform: perspective(400px) rotateY(0deg);
+    opacity: 1;
+  }
+}
+
+.flipInY {
+  -webkit-backface-visibility: visible !important;
+  -ms-backface-visibility: visible !important;
+  backface-visibility: visible !important;
+  -webkit-animation-name: flipInY;
+  animation-name: flipInY;
+}
+
+@-webkit-keyframes flipOutX {
+  0% {
+    -webkit-transform: perspective(400px) rotateX(0deg);
+    transform: perspective(400px) rotateX(0deg);
+    opacity: 1;
+  }
+
+  100% {
+    -webkit-transform: perspective(400px) rotateX(90deg);
+    transform: perspective(400px) rotateX(90deg);
+    opacity: 0;
+  }
+}
+
+@keyframes flipOutX {
+  0% {
+    -webkit-transform: perspective(400px) rotateX(0deg);
+    -ms-transform: perspective(400px) rotateX(0deg);
+    transform: perspective(400px) rotateX(0deg);
+    opacity: 1;
+  }
+
+  100% {
+    -webkit-transform: perspective(400px) rotateX(90deg);
+    -ms-transform: perspective(400px) rotateX(90deg);
+    transform: perspective(400px) rotateX(90deg);
+    opacity: 0;
+  }
+}
+
+.flipOutX {
+  -webkit-animation-name: flipOutX;
+  animation-name: flipOutX;
+  -webkit-backface-visibility: visible !important;
+  -ms-backface-visibility: visible !important;
+  backface-visibility: visible !important;
+}
+
+@-webkit-keyframes flipOutY {
+  0% {
+    -webkit-transform: perspective(400px) rotateY(0deg);
+    transform: perspective(400px) rotateY(0deg);
+    opacity: 1;
+  }
+
+  100% {
+    -webkit-transform: perspective(400px) rotateY(90deg);
+    transform: perspective(400px) rotateY(90deg);
+    opacity: 0;
+  }
+}
+
+@keyframes flipOutY {
+  0% {
+    -webkit-transform: perspective(400px) rotateY(0deg);
+    -ms-transform: perspective(400px) rotateY(0deg);
+    transform: perspective(400px) rotateY(0deg);
+    opacity: 1;
+  }
+
+  100% {
+    -webkit-transform: perspective(400px) rotateY(90deg);
+    -ms-transform: perspective(400px) rotateY(90deg);
+    transform: perspective(400px) rotateY(90deg);
+    opacity: 0;
+  }
+}
+
+.flipOutY {
+  -webkit-backface-visibility: visible !important;
+  -ms-backface-visibility: visible !important;
+  backface-visibility: visible !important;
+  -webkit-animation-name: flipOutY;
+  animation-name: flipOutY;
+}
+
+@-webkit-keyframes lightSpeedIn {
+  0% {
+    -webkit-transform: translateX(100%) skewX(-30deg);
+    transform: translateX(100%) skewX(-30deg);
+    opacity: 0;
+  }
+
+  60% {
+    -webkit-transform: translateX(-20%) skewX(30deg);
+    transform: translateX(-20%) skewX(30deg);
+    opacity: 1;
+  }
+
+  80% {
+    -webkit-transform: translateX(0%) skewX(-15deg);
+    transform: translateX(0%) skewX(-15deg);
+    opacity: 1;
+  }
+
+  100% {
+    -webkit-transform: translateX(0%) skewX(0deg);
+    transform: translateX(0%) skewX(0deg);
+    opacity: 1;
+  }
+}
+
+@keyframes lightSpeedIn {
+  0% {
+    -webkit-transform: translateX(100%) skewX(-30deg);
+    -ms-transform: translateX(100%) skewX(-30deg);
+    transform: translateX(100%) skewX(-30deg);
+    opacity: 0;
+  }
+
+  60% {
+    -webkit-transform: translateX(-20%) skewX(30deg);
+    -ms-transform: translateX(-20%) skewX(30deg);
+    transform: translateX(-20%) skewX(30deg);
+    opacity: 1;
+  }
+
+  80% {
+    -webkit-transform: translateX(0%) skewX(-15deg);
+    -ms-transform: translateX(0%) skewX(-15deg);
+    transform: translateX(0%) skewX(-15deg);
+    opacity: 1;
+  }
+
+  100% {
+    -webkit-transform: translateX(0%) skewX(0deg);
+    -ms-transform: translateX(0%) skewX(0deg);
+    transform: translateX(0%) skewX(0deg);
+    opacity: 1;
+  }
+}
+
+.lightSpeedIn {
+  -webkit-animation-name: lightSpeedIn;
+  animation-name: lightSpeedIn;
+  -webkit-animation-timing-function: ease-out;
+  animation-timing-function: ease-out;
+}
+
+@-webkit-keyframes lightSpeedOut {
+  0% {
+    -webkit-transform: translateX(0%) skewX(0deg);
+    transform: translateX(0%) skewX(0deg);
+    opacity: 1;
+  }
+
+  100% {
+    -webkit-transform: translateX(100%) skewX(-30deg);
+    transform: translateX(100%) skewX(-30deg);
+    opacity: 0;
+  }
+}
+
+@keyframes lightSpeedOut {
+  0% {
+    -webkit-transform: translateX(0%) skewX(0deg);
+    -ms-transform: translateX(0%) skewX(0deg);
+    transform: translateX(0%) skewX(0deg);
+    opacity: 1;
+  }
+
+  100% {
+    -webkit-transform: translateX(100%) skewX(-30deg);
+    -ms-transform: translateX(100%) skewX(-30deg);
+    transform: translateX(100%) skewX(-30deg);
+    opacity: 0;
+  }
+}
+
+.lightSpeedOut {
+  -webkit-animation-name: lightSpeedOut;
+  animation-name: lightSpeedOut;
+  -webkit-animation-timing-function: ease-in;
+  animation-timing-function: ease-in;
+}
+
+@-webkit-keyframes rotateIn {
+  0% {
+    -webkit-transform-origin: center center;
+    transform-origin: center center;
+    -webkit-transform: rotate(-200deg);
+    transform: rotate(-200deg);
+    opacity: 0;
+  }
+
+  100% {
+    -webkit-transform-origin: center center;
+    transform-origin: center center;
+    -webkit-transform: rotate(0);
+    transform: rotate(0);
+    opacity: 1;
+  }
+}
+
+@keyframes rotateIn {
+  0% {
+    -webkit-transform-origin: center center;
+    -ms-transform-origin: center center;
+    transform-origin: center center;
+    -webkit-transform: rotate(-200deg);
+    -ms-transform: rotate(-200deg);
+    transform: rotate(-200deg);
+    opacity: 0;
+  }
+
+  100% {
+    -webkit-transform-origin: center center;
+    -ms-transform-origin: center center;
+    transform-origin: center center;
+    -webkit-transform: rotate(0);
+    -ms-transform: rotate(0);
+    transform: rotate(0);
+    opacity: 1;
+  }
+}
+
+.rotateIn {
+  -webkit-animation-name: rotateIn;
+  animation-name: rotateIn;
+}
+
+@-webkit-keyframes rotateInDownLeft {
+  0% {
+    -webkit-transform-origin: left bottom;
+    transform-origin: left bottom;
+    -webkit-transform: rotate(-90deg);
+    transform: rotate(-90deg);
+    opacity: 0;
+  }
+
+  100% {
+    -webkit-transform-origin: left bottom;
+    transform-origin: left bottom;
+    -webkit-transform: rotate(0);
+    transform: rotate(0);
+    opacity: 1;
+  }
+}
+
+@keyframes rotateInDownLeft {
+  0% {
+    -webkit-transform-origin: left bottom;
+    -ms-transform-origin: left bottom;
+    transform-origin: left bottom;
+    -webkit-transform: rotate(-90deg);
+    -ms-transform: rotate(-90deg);
+    transform: rotate(-90deg);
+    opacity: 0;
+  }
+
+  100% {
+    -webkit-transform-origin: left bottom;
+    -ms-transform-origin: left bottom;
+    transform-origin: left bottom;
+    -webkit-transform: rotate(0);
+    -ms-transform: rotate(0);
+    transform: rotate(0);
+    opacity: 1;
+  }
+}
+
+.rotateInDownLeft {
+  -webkit-animation-name: rotateInDownLeft;
+  animation-name: rotateInDownLeft;
+}
+
+@-webkit-keyframes rotateInDownRight {
+  0% {
+    -webkit-transform-origin: right bottom;
+    transform-origin: right bottom;
+    -webkit-transform: rotate(90deg);
+    transform: rotate(90deg);
+    opacity: 0;
+  }
+
+  100% {
+    -webkit-transform-origin: right bottom;
+    transform-origin: right bottom;
+    -webkit-transform: rotate(0);
+    transform: rotate(0);
+    opacity: 1;
+  }
+}
+
+@keyframes rotateInDownRight {
+  0% {
+    -webkit-transform-origin: right bottom;
+    -ms-transform-origin: right bottom;
+    transform-origin: right bottom;
+    -webkit-transform: rotate(90deg);
+    -ms-transform: rotate(90deg);
+    transform: rotate(90deg);
+    opacity: 0;
+  }
+
+  100% {
+    -webkit-transform-origin: right bottom;
+    -ms-transform-origin: right bottom;
+    transform-origin: right bottom;
+    -webkit-transform: rotate(0);
+    -ms-transform: rotate(0);
+    transform: rotate(0);
+    opacity: 1;
+  }
+}
+
+.rotateInDownRight {
+  -webkit-animation-name: rotateInDownRight;
+  animation-name: rotateInDownRight;
+}
+
+@-webkit-keyframes rotateInUpLeft {
+  0% {
+    -webkit-transform-origin: left bottom;
+    transform-origin: left bottom;
+    -webkit-transform: rotate(90deg);
+    transform: rotate(90deg);
+    opacity: 0;
+  }
+
+  100% {
+    -webkit-transform-origin: left bottom;
+    transform-origin: left bottom;
+    -webkit-transform: rotate(0);
+    transform: rotate(0);
+    opacity: 1;
+  }
+}
+
+@keyframes rotateInUpLeft {
+  0% {
+    -webkit-transform-origin: left bottom;
+    -ms-transform-origin: left bottom;
+    transform-origin: left bottom;
+    -webkit-transform: rotate(90deg);
+    -ms-transform: rotate(90deg);
+    transform: rotate(90deg);
+    opacity: 0;
+  }
+
+  100% {
+    -webkit-transform-origin: left bottom;
+    -ms-transform-origin: left bottom;
+    transform-origin: left bottom;
+    -webkit-transform: rotate(0);
+    -ms-transform: rotate(0);
+    transform: rotate(0);
+    opacity: 1;
+  }
+}
+
+.rotateInUpLeft {
+  -webkit-animation-name: rotateInUpLeft;
+  animation-name: rotateInUpLeft;
+}
+
+@-webkit-keyframes rotateInUpRight {
+  0% {
+    -webkit-transform-origin: right bottom;
+    transform-origin: right bottom;
+    -webkit-transform: rotate(-90deg);
+    transform: rotate(-90deg);
+    opacity: 0;
+  }
+
+  100% {
+    -webkit-transform-origin: right bottom;
+    transform-origin: right bottom;
+    -webkit-transform: rotate(0);
+    transform: rotate(0);
+    opacity: 1;
+  }
+}
+
+@keyframes rotateInUpRight {
+  0% {
+    -webkit-transform-origin: right bottom;
+    -ms-transform-origin: right bottom;
+    transform-origin: right bottom;
+    -webkit-transform: rotate(-90deg);
+    -ms-transform: rotate(-90deg);
+    transform: rotate(-90deg);
+    opacity: 0;
+  }
+
+  100% {
+    -webkit-transform-origin: right bottom;
+    -ms-transform-origin: right bottom;
+    transform-origin: right bottom;
+    -webkit-transform: rotate(0);
+    -ms-transform: rotate(0);
+    transform: rotate(0);
+    opacity: 1;
+  }
+}
+
+.rotateInUpRight {
+  -webkit-animation-name: rotateInUpRight;
+  animation-name: rotateInUpRight;
+}
+
+@-webkit-keyframes rotateOut {
+  0% {
+    -webkit-transform-origin: center center;
+    transform-origin: center center;
+    -webkit-transform: rotate(0);
+    transform: rotate(0);
+    opacity: 1;
+  }
+
+  100% {
+    -webkit-transform-origin: center center;
+    transform-origin: center center;
+    -webkit-transform: rotate(200deg);
+    transform: rotate(200deg);
+    opacity: 0;
+  }
+}
+
+@keyframes rotateOut {
+  0% {
+    -webkit-transform-origin: center center;
+    -ms-transform-origin: center center;
+    transform-origin: center center;
+    -webkit-transform: rotate(0);
+    -ms-transform: rotate(0);
+    transform: rotate(0);
+    opacity: 1;
+  }
+
+  100% {
+    -webkit-transform-origin: center center;
+    -ms-transform-origin: center center;
+    transform-origin: center center;
+    -webkit-transform: rotate(200deg);
+    -ms-transform: rotate(200deg);
+    transform: rotate(200deg);
+    opacity: 0;
+  }
+}
+
+.rotateOut {
+  -webkit-animation-name: rotateOut;
+  animation-name: rotateOut;
+}
+
+@-webkit-keyframes rotateOutDownLeft {
+  0% {
+    -webkit-transform-origin: left bottom;
+    transform-origin: left bottom;
+    -webkit-transform: rotate(0);
+    transform: rotate(0);
+    opacity: 1;
+  }
+
+  100% {
+    -webkit-transform-origin: left bottom;
+    transform-origin: left bottom;
+    -webkit-transform: rotate(90deg);
+    transform: rotate(90deg);
+    opacity: 0;
+  }
+}
+
+@keyframes rotateOutDownLeft {
+  0% {
+    -webkit-transform-origin: left bottom;
+    -ms-transform-origin: left bottom;
+    transform-origin: left bottom;
+    -webkit-transform: rotate(0);
+    -ms-transform: rotate(0);
+    transform: rotate(0);
+    opacity: 1;
+  }
+
+  100% {
+    -webkit-transform-origin: left bottom;
+    -ms-transform-origin: left bottom;
+    transform-origin: left bottom;
+    -webkit-transform: rotate(90deg);
+    -ms-transform: rotate(90deg);
+    transform: rotate(90deg);
+    opacity: 0;
+  }
+}
+
+.rotateOutDownLeft {
+  -webkit-animation-name: rotateOutDownLeft;
+  animation-name: rotateOutDownLeft;
+}
+
+@-webkit-keyframes rotateOutDownRight {
+  0% {
+    -webkit-transform-origin: right bottom;
+    transform-origin: right bottom;
+    -webkit-transform: rotate(0);
+    transform: rotate(0);
+    opacity: 1;
+  }
+
+  100% {
+    -webkit-transform-origin: right bottom;
+    transform-origin: right bottom;
+    -webkit-transform: rotate(-90deg);
+    transform: rotate(-90deg);
+    opacity: 0;
+  }
+}
+
+@keyframes rotateOutDownRight {
+  0% {
+    -webkit-transform-origin: right bottom;
+    -ms-transform-origin: right bottom;
+    transform-origin: right bottom;
+    -webkit-transform: rotate(0);
+    -ms-transform: rotate(0);
+    transform: rotate(0);
+    opacity: 1;
+  }
+
+  100% {
+    -webkit-transform-origin: right bottom;
+    -ms-transform-origin: right bottom;
+    transform-origin: right bottom;
+    -webkit-transform: rotate(-90deg);
+    -ms-transform: rotate(-90deg);
+    transform: rotate(-90deg);
+    opacity: 0;
+  }
+}
+
+.rotateOutDownRight {
+  -webkit-animation-name: rotateOutDownRight;
+  animation-name: rotateOutDownRight;
+}
+
+@-webkit-keyframes rotateOutUpLeft {
+  0% {
+    -webkit-transform-origin: left bottom;
+    transform-origin: left bottom;
+    -webkit-transform: rotate(0);
+    transform: rotate(0);
+    opacity: 1;
+  }
+
+  100% {
+    -webkit-transform-origin: left bottom;
+    transform-origin: left bottom;
+    -webkit-transform: rotate(-90deg);
+    transform: rotate(-90deg);
+    opacity: 0;
+  }
+}
+
+@keyframes rotateOutUpLeft {
+  0% {
+    -webkit-transform-origin: left bottom;
+    -ms-transform-origin: left bottom;
+    transform-origin: left bottom;
+    -webkit-transform: rotate(0);
+    -ms-transform: rotate(0);
+    transform: rotate(0);
+    opacity: 1;
+  }
+
+  100% {
+    -webkit-transform-origin: left bottom;
+    -ms-transform-origin: left bottom;
+    transform-origin: left bottom;
+    -webkit-transform: rotate(-90deg);
+    -ms-transform: rotate(-90deg);
+    transform: rotate(-90deg);
+    opacity: 0;
+  }
+}
+
+.rotateOutUpLeft {
+  -webkit-animation-name: rotateOutUpLeft;
+  animation-name: rotateOutUpLeft;
+}
+
+@-webkit-keyframes rotateOutUpRight {
+  0% {
+    -webkit-transform-origin: right bottom;
+    transform-origin: right bottom;
+    -webkit-transform: rotate(0);
+    transform: rotate(0);
+    opacity: 1;
+  }
+
+  100% {
+    -webkit-transform-origin: right bottom;
+    transform-origin: right bottom;
+    -webkit-transform: rotate(90deg);
+    transform: rotate(90deg);
+    opacity: 0;
+  }
+}
+
+@keyframes rotateOutUpRight {
+  0% {
+    -webkit-transform-origin: right bottom;
+    -ms-transform-origin: right bottom;
+    transform-origin: right bottom;
+    -webkit-transform: rotate(0);
+    -ms-transform: rotate(0);
+    transform: rotate(0);
+    opacity: 1;
+  }
+
+  100% {
+    -webkit-transform-origin: right bottom;
+    -ms-transform-origin: right bottom;
+    transform-origin: right bottom;
+    -webkit-transform: rotate(90deg);
+    -ms-transform: rotate(90deg);
+    transform: rotate(90deg);
+    opacity: 0;
+  }
+}
+
+.rotateOutUpRight {
+  -webkit-animation-name: rotateOutUpRight;
+  animation-name: rotateOutUpRight;
+}
+
+@-webkit-keyframes slideInDown {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateY(-2000px);
+    transform: translateY(-2000px);
+  }
+
+  100% {
+    -webkit-transform: translateY(0);
+    transform: translateY(0);
+  }
+}
+
+@keyframes slideInDown {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateY(-2000px);
+    -ms-transform: translateY(-2000px);
+    transform: translateY(-2000px);
+  }
+
+  100% {
+    -webkit-transform: translateY(0);
+    -ms-transform: translateY(0);
+    transform: translateY(0);
+  }
+}
+
+.slideInDown {
+  -webkit-animation-name: slideInDown;
+  animation-name: slideInDown;
+}
+
+@-webkit-keyframes slideInLeft {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateX(-2000px);
+    transform: translateX(-2000px);
+  }
+
+  100% {
+    -webkit-transform: translateX(0);
+    transform: translateX(0);
+  }
+}
+
+@keyframes slideInLeft {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateX(-2000px);
+    -ms-transform: translateX(-2000px);
+    transform: translateX(-2000px);
+  }
+
+  100% {
+    -webkit-transform: translateX(0);
+    -ms-transform: translateX(0);
+    transform: translateX(0);
+  }
+}
+
+.slideInLeft {
+  -webkit-animation-name: slideInLeft;
+  animation-name: slideInLeft;
+}
+
+@-webkit-keyframes slideInRight {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateX(2000px);
+    transform: translateX(2000px);
+  }
+
+  100% {
+    -webkit-transform: translateX(0);
+    transform: translateX(0);
+  }
+}
+
+@keyframes slideInRight {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateX(2000px);
+    -ms-transform: translateX(2000px);
+    transform: translateX(2000px);
+  }
+
+  100% {
+    -webkit-transform: translateX(0);
+    -ms-transform: translateX(0);
+    transform: translateX(0);
+  }
+}
+
+.slideInRight {
+  -webkit-animation-name: slideInRight;
+  animation-name: slideInRight;
+}
+
+@-webkit-keyframes slideOutLeft {
+  0% {
+    -webkit-transform: translateX(0);
+    transform: translateX(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateX(-2000px);
+    transform: translateX(-2000px);
+  }
+}
+
+@keyframes slideOutLeft {
+  0% {
+    -webkit-transform: translateX(0);
+    -ms-transform: translateX(0);
+    transform: translateX(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateX(-2000px);
+    -ms-transform: translateX(-2000px);
+    transform: translateX(-2000px);
+  }
+}
+
+.slideOutLeft {
+  -webkit-animation-name: slideOutLeft;
+  animation-name: slideOutLeft;
+}
+
+@-webkit-keyframes slideOutRight {
+  0% {
+    -webkit-transform: translateX(0);
+    transform: translateX(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateX(2000px);
+    transform: translateX(2000px);
+  }
+}
+
+@keyframes slideOutRight {
+  0% {
+    -webkit-transform: translateX(0);
+    -ms-transform: translateX(0);
+    transform: translateX(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateX(2000px);
+    -ms-transform: translateX(2000px);
+    transform: translateX(2000px);
+  }
+}
+
+.slideOutRight {
+  -webkit-animation-name: slideOutRight;
+  animation-name: slideOutRight;
+}
+
+@-webkit-keyframes slideOutUp {
+  0% {
+    -webkit-transform: translateY(0);
+    transform: translateY(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateY(-2000px);
+    transform: translateY(-2000px);
+  }
+}
+
+@keyframes slideOutUp {
+  0% {
+    -webkit-transform: translateY(0);
+    -ms-transform: translateY(0);
+    transform: translateY(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateY(-2000px);
+    -ms-transform: translateY(-2000px);
+    transform: translateY(-2000px);
+  }
+}
+
+.slideOutUp {
+  -webkit-animation-name: slideOutUp;
+  animation-name: slideOutUp;
+}
+
+@-webkit-keyframes slideInUp {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateY(2000px);
+    transform: translateY(2000px);
+  }
+
+  100% {
+    opacity: 1;
+    -webkit-transform: translateY(0);
+    transform: translateY(0);
+  }
+}
+
+@keyframes slideInUp {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateY(2000px);
+    -ms-transform: translateY(2000px);
+    transform: translateY(2000px);
+  }
+
+  100% {
+    opacity: 1;
+    -webkit-transform: translateY(0);
+    -ms-transform: translateY(0);
+    transform: translateY(0);
+  }
+}
+
+.slideInUp {
+  -webkit-animation-name: slideInUp;
+  animation-name: slideInUp;
+}
+
+@-webkit-keyframes slideOutDown {
+  0% {
+    -webkit-transform: translateY(0);
+    transform: translateY(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateY(2000px);
+    transform: translateY(2000px);
+  }
+}
+
+@keyframes slideOutDown {
+  0% {
+    -webkit-transform: translateY(0);
+    -ms-transform: translateY(0);
+    transform: translateY(0);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateY(2000px);
+    -ms-transform: translateY(2000px);
+    transform: translateY(2000px);
+  }
+}
+
+.slideOutDown {
+  -webkit-animation-name: slideOutDown;
+  animation-name: slideOutDown;
+}
+
+@-webkit-keyframes hinge {
+  0% {
+    -webkit-transform: rotate(0);
+    transform: rotate(0);
+    -webkit-transform-origin: top left;
+    transform-origin: top left;
+    -webkit-animation-timing-function: ease-in-out;
+    animation-timing-function: ease-in-out;
+  }
+
+  20%, 60% {
+    -webkit-transform: rotate(80deg);
+    transform: rotate(80deg);
+    -webkit-transform-origin: top left;
+    transform-origin: top left;
+    -webkit-animation-timing-function: ease-in-out;
+    animation-timing-function: ease-in-out;
+  }
+
+  40% {
+    -webkit-transform: rotate(60deg);
+    transform: rotate(60deg);
+    -webkit-transform-origin: top left;
+    transform-origin: top left;
+    -webkit-animation-timing-function: ease-in-out;
+    animation-timing-function: ease-in-out;
+  }
+
+  80% {
+    -webkit-transform: rotate(60deg) translateY(0);
+    transform: rotate(60deg) translateY(0);
+    -webkit-transform-origin: top left;
+    transform-origin: top left;
+    -webkit-animation-timing-function: ease-in-out;
+    animation-timing-function: ease-in-out;
+    opacity: 1;
+  }
+
+  100% {
+    -webkit-transform: translateY(700px);
+    transform: translateY(700px);
+    opacity: 0;
+  }
+}
+
+@keyframes hinge {
+  0% {
+    -webkit-transform: rotate(0);
+    -ms-transform: rotate(0);
+    transform: rotate(0);
+    -webkit-transform-origin: top left;
+    -ms-transform-origin: top left;
+    transform-origin: top left;
+    -webkit-animation-timing-function: ease-in-out;
+    animation-timing-function: ease-in-out;
+  }
+
+  20%, 60% {
+    -webkit-transform: rotate(80deg);
+    -ms-transform: rotate(80deg);
+    transform: rotate(80deg);
+    -webkit-transform-origin: top left;
+    -ms-transform-origin: top left;
+    transform-origin: top left;
+    -webkit-animation-timing-function: ease-in-out;
+    animation-timing-function: ease-in-out;
+  }
+
+  40% {
+    -webkit-transform: rotate(60deg);
+    -ms-transform: rotate(60deg);
+    transform: rotate(60deg);
+    -webkit-transform-origin: top left;
+    -ms-transform-origin: top left;
+    transform-origin: top left;
+    -webkit-animation-timing-function: ease-in-out;
+    animation-timing-function: ease-in-out;
+  }
+
+  80% {
+    -webkit-transform: rotate(60deg) translateY(0);
+    -ms-transform: rotate(60deg) translateY(0);
+    transform: rotate(60deg) translateY(0);
+    -webkit-transform-origin: top left;
+    -ms-transform-origin: top left;
+    transform-origin: top left;
+    -webkit-animation-timing-function: ease-in-out;
+    animation-timing-function: ease-in-out;
+    opacity: 1;
+  }
+
+  100% {
+    -webkit-transform: translateY(700px);
+    -ms-transform: translateY(700px);
+    transform: translateY(700px);
+    opacity: 0;
+  }
+}
+
+.hinge {
+  -webkit-animation-name: hinge;
+  animation-name: hinge;
+}
+
+/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */
+
+@-webkit-keyframes rollIn {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateX(-100%) rotate(-120deg);
+    transform: translateX(-100%) rotate(-120deg);
+  }
+
+  100% {
+    opacity: 1;
+    -webkit-transform: translateX(0px) rotate(0deg);
+    transform: translateX(0px) rotate(0deg);
+  }
+}
+
+@keyframes rollIn {
+  0% {
+    opacity: 0;
+    -webkit-transform: translateX(-100%) rotate(-120deg);
+    -ms-transform: translateX(-100%) rotate(-120deg);
+    transform: translateX(-100%) rotate(-120deg);
+  }
+
+  100% {
+    opacity: 1;
+    -webkit-transform: translateX(0px) rotate(0deg);
+    -ms-transform: translateX(0px) rotate(0deg);
+    transform: translateX(0px) rotate(0deg);
+  }
+}
+
+.rollIn {
+  -webkit-animation-name: rollIn;
+  animation-name: rollIn;
+}
+
+/* originally authored by Nick Pettit - https://github.com/nickpettit/glide */
+
+@-webkit-keyframes rollOut {
+  0% {
+    opacity: 1;
+    -webkit-transform: translateX(0px) rotate(0deg);
+    transform: translateX(0px) rotate(0deg);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateX(100%) rotate(120deg);
+    transform: translateX(100%) rotate(120deg);
+  }
+}
+
+@keyframes rollOut {
+  0% {
+    opacity: 1;
+    -webkit-transform: translateX(0px) rotate(0deg);
+    -ms-transform: translateX(0px) rotate(0deg);
+    transform: translateX(0px) rotate(0deg);
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: translateX(100%) rotate(120deg);
+    -ms-transform: translateX(100%) rotate(120deg);
+    transform: translateX(100%) rotate(120deg);
+  }
+}
+
+.rollOut {
+  -webkit-animation-name: rollOut;
+  animation-name: rollOut;
+}
+
+@-webkit-keyframes zoomIn {
+  0% {
+    opacity: 0;
+    -webkit-transform: scale(.3);
+    transform: scale(.3);
+  }
+
+  50% {
+    opacity: 1;
+  }
+}
+
+@keyframes zoomIn {
+  0% {
+    opacity: 0;
+    -webkit-transform: scale(.3);
+    -ms-transform: scale(.3);
+    transform: scale(.3);
+  }
+
+  50% {
+    opacity: 1;
+  }
+}
+
+.zoomIn {
+  -webkit-animation-name: zoomIn;
+  animation-name: zoomIn;
+}
+
+@-webkit-keyframes zoomInDown {
+  0% {
+    opacity: 0;
+    -webkit-transform: scale(.1) translateY(-2000px);
+    transform: scale(.1) translateY(-2000px);
+    -webkit-animation-timing-function: ease-in-out;
+    animation-timing-function: ease-in-out;
+  }
+
+  60% {
+    opacity: 1;
+    -webkit-transform: scale(.475) translateY(60px);
+    transform: scale(.475) translateY(60px);
+    -webkit-animation-timing-function: ease-out;
+    animation-timing-function: ease-out;
+  }
+}
+
+@keyframes zoomInDown {
+  0% {
+    opacity: 0;
+    -webkit-transform: scale(.1) translateY(-2000px);
+    -ms-transform: scale(.1) translateY(-2000px);
+    transform: scale(.1) translateY(-2000px);
+    -webkit-animation-timing-function: ease-in-out;
+    animation-timing-function: ease-in-out;
+  }
+
+  60% {
+    opacity: 1;
+    -webkit-transform: scale(.475) translateY(60px);
+    -ms-transform: scale(.475) translateY(60px);
+    transform: scale(.475) translateY(60px);
+    -webkit-animation-timing-function: ease-out;
+    animation-timing-function: ease-out;
+  }
+}
+
+.zoomInDown {
+  -webkit-animation-name: zoomInDown;
+  animation-name: zoomInDown;
+}
+
+@-webkit-keyframes zoomInLeft {
+  0% {
+    opacity: 0;
+    -webkit-transform: scale(.1) translateX(-2000px);
+    transform: scale(.1) translateX(-2000px);
+    -webkit-animation-timing-function: ease-in-out;
+    animation-timing-function: ease-in-out;
+  }
+
+  60% {
+    opacity: 1;
+    -webkit-transform: scale(.475) translateX(48px);
+    transform: scale(.475) translateX(48px);
+    -webkit-animation-timing-function: ease-out;
+    animation-timing-function: ease-out;
+  }
+}
+
+@keyframes zoomInLeft {
+  0% {
+    opacity: 0;
+    -webkit-transform: scale(.1) translateX(-2000px);
+    -ms-transform: scale(.1) translateX(-2000px);
+    transform: scale(.1) translateX(-2000px);
+    -webkit-animation-timing-function: ease-in-out;
+    animation-timing-function: ease-in-out;
+  }
+
+  60% {
+    opacity: 1;
+    -webkit-transform: scale(.475) translateX(48px);
+    -ms-transform: scale(.475) translateX(48px);
+    transform: scale(.475) translateX(48px);
+    -webkit-animation-timing-function: ease-out;
+    animation-timing-function: ease-out;
+  }
+}
+
+.zoomInLeft {
+  -webkit-animation-name: zoomInLeft;
+  animation-name: zoomInLeft;
+}
+
+@-webkit-keyframes zoomInRight {
+  0% {
+    opacity: 0;
+    -webkit-transform: scale(.1) translateX(2000px);
+    transform: scale(.1) translateX(2000px);
+    -webkit-animation-timing-function: ease-in-out;
+    animation-timing-function: ease-in-out;
+  }
+
+  60% {
+    opacity: 1;
+    -webkit-transform: scale(.475) translateX(-48px);
+    transform: scale(.475) translateX(-48px);
+    -webkit-animation-timing-function: ease-out;
+    animation-timing-function: ease-out;
+  }
+}
+
+@keyframes zoomInRight {
+  0% {
+    opacity: 0;
+    -webkit-transform: scale(.1) translateX(2000px);
+    -ms-transform: scale(.1) translateX(2000px);
+    transform: scale(.1) translateX(2000px);
+    -webkit-animation-timing-function: ease-in-out;
+    animation-timing-function: ease-in-out;
+  }
+
+  60% {
+    opacity: 1;
+    -webkit-transform: scale(.475) translateX(-48px);
+    -ms-transform: scale(.475) translateX(-48px);
+    transform: scale(.475) translateX(-48px);
+    -webkit-animation-timing-function: ease-out;
+    animation-timing-function: ease-out;
+  }
+}
+
+.zoomInRight {
+  -webkit-animation-name: zoomInRight;
+  animation-name: zoomInRight;
+}
+
+@-webkit-keyframes zoomInUp {
+  0% {
+    opacity: 0;
+    -webkit-transform: scale(.1) translateY(2000px);
+    transform: scale(.1) translateY(2000px);
+    -webkit-animation-timing-function: ease-in-out;
+    animation-timing-function: ease-in-out;
+  }
+
+  60% {
+    opacity: 1;
+    -webkit-transform: scale(.475) translateY(-60px);
+    transform: scale(.475) translateY(-60px);
+    -webkit-animation-timing-function: ease-out;
+    animation-timing-function: ease-out;
+  }
+}
+
+@keyframes zoomInUp {
+  0% {
+    opacity: 0;
+    -webkit-transform: scale(.1) translateY(2000px);
+    -ms-transform: scale(.1) translateY(2000px);
+    transform: scale(.1) translateY(2000px);
+    -webkit-animation-timing-function: ease-in-out;
+    animation-timing-function: ease-in-out;
+  }
+
+  60% {
+    opacity: 1;
+    -webkit-transform: scale(.475) translateY(-60px);
+    -ms-transform: scale(.475) translateY(-60px);
+    transform: scale(.475) translateY(-60px);
+    -webkit-animation-timing-function: ease-out;
+    animation-timing-function: ease-out;
+  }
+}
+
+.zoomInUp {
+  -webkit-animation-name: zoomInUp;
+  animation-name: zoomInUp;
+}
+
+@-webkit-keyframes zoomOut {
+  0% {
+    opacity: 1;
+    -webkit-transform: scale(1);
+    transform: scale(1);
+  }
+
+  50% {
+    opacity: 0;
+    -webkit-transform: scale(.3);
+    transform: scale(.3);
+  }
+
+  100% {
+    opacity: 0;
+  }
+}
+
+@keyframes zoomOut {
+  0% {
+    opacity: 1;
+    -webkit-transform: scale(1);
+    -ms-transform: scale(1);
+    transform: scale(1);
+  }
+
+  50% {
+    opacity: 0;
+    -webkit-transform: scale(.3);
+    -ms-transform: scale(.3);
+    transform: scale(.3);
+  }
+
+  100% {
+    opacity: 0;
+  }
+}
+
+.zoomOut {
+  -webkit-animation-name: zoomOut;
+  animation-name: zoomOut;
+}
+
+@-webkit-keyframes zoomOutDown {
+  40% {
+    opacity: 1;
+    -webkit-transform: scale(.475) translateY(-60px);
+    transform: scale(.475) translateY(-60px);
+    -webkit-animation-timing-function: linear;
+    animation-timing-function: linear;
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: scale(.1) translateY(2000px);
+    transform: scale(.1) translateY(2000px);
+    -webkit-transform-origin: center bottom;
+    transform-origin: center bottom;
+  }
+}
+
+@keyframes zoomOutDown {
+  40% {
+    opacity: 1;
+    -webkit-transform: scale(.475) translateY(-60px);
+    -ms-transform: scale(.475) translateY(-60px);
+    transform: scale(.475) translateY(-60px);
+    -webkit-animation-timing-function: linear;
+    animation-timing-function: linear;
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: scale(.1) translateY(2000px);
+    -ms-transform: scale(.1) translateY(2000px);
+    transform: scale(.1) translateY(2000px);
+    -webkit-transform-origin: center bottom;
+    -ms-transform-origin: center bottom;
+    transform-origin: center bottom;
+  }
+}
+
+.zoomOutDown {
+  -webkit-animation-name: zoomOutDown;
+  animation-name: zoomOutDown;
+}
+
+@-webkit-keyframes zoomOutLeft {
+  40% {
+    opacity: 1;
+    -webkit-transform: scale(.475) translateX(42px);
+    transform: scale(.475) translateX(42px);
+    -webkit-animation-timing-function: linear;
+    animation-timing-function: linear;
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: scale(.1) translateX(-2000px);
+    transform: scale(.1) translateX(-2000px);
+    -webkit-transform-origin: left center;
+    transform-origin: left center;
+  }
+}
+
+@keyframes zoomOutLeft {
+  40% {
+    opacity: 1;
+    -webkit-transform: scale(.475) translateX(42px);
+    -ms-transform: scale(.475) translateX(42px);
+    transform: scale(.475) translateX(42px);
+    -webkit-animation-timing-function: linear;
+    animation-timing-function: linear;
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: scale(.1) translateX(-2000px);
+    -ms-transform: scale(.1) translateX(-2000px);
+    transform: scale(.1) translateX(-2000px);
+    -webkit-transform-origin: left center;
+    -ms-transform-origin: left center;
+    transform-origin: left center;
+  }
+}
+
+.zoomOutLeft {
+  -webkit-animation-name: zoomOutLeft;
+  animation-name: zoomOutLeft;
+}
+
+@-webkit-keyframes zoomOutRight {
+  40% {
+    opacity: 1;
+    -webkit-transform: scale(.475) translateX(-42px);
+    transform: scale(.475) translateX(-42px);
+    -webkit-animation-timing-function: linear;
+    animation-timing-function: linear;
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: scale(.1) translateX(2000px);
+    transform: scale(.1) translateX(2000px);
+    -webkit-transform-origin: right center;
+    transform-origin: right center;
+  }
+}
+
+@keyframes zoomOutRight {
+  40% {
+    opacity: 1;
+    -webkit-transform: scale(.475) translateX(-42px);
+    -ms-transform: scale(.475) translateX(-42px);
+    transform: scale(.475) translateX(-42px);
+    -webkit-animation-timing-function: linear;
+    animation-timing-function: linear;
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: scale(.1) translateX(2000px);
+    -ms-transform: scale(.1) translateX(2000px);
+    transform: scale(.1) translateX(2000px);
+    -webkit-transform-origin: right center;
+    -ms-transform-origin: right center;
+    transform-origin: right center;
+  }
+}
+
+.zoomOutRight {
+  -webkit-animation-name: zoomOutRight;
+  animation-name: zoomOutRight;
+}
+
+@-webkit-keyframes zoomOutUp {
+  40% {
+    opacity: 1;
+    -webkit-transform: scale(.475) translateY(60px);
+    transform: scale(.475) translateY(60px);
+    -webkit-animation-timing-function: linear;
+    animation-timing-function: linear;
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: scale(.1) translateY(-2000px);
+    transform: scale(.1) translateY(-2000px);
+    -webkit-transform-origin: center top;
+    transform-origin: center top;
+  }
+}
+
+@keyframes zoomOutUp {
+  40% {
+    opacity: 1;
+    -webkit-transform: scale(.475) translateY(60px);
+    -ms-transform: scale(.475) translateY(60px);
+    transform: scale(.475) translateY(60px);
+    -webkit-animation-timing-function: linear;
+    animation-timing-function: linear;
+  }
+
+  100% {
+    opacity: 0;
+    -webkit-transform: scale(.1) translateY(-2000px);
+    -ms-transform: scale(.1) translateY(-2000px);
+    transform: scale(.1) translateY(-2000px);
+    -webkit-transform-origin: center top;
+    -ms-transform-origin: center top;
+    transform-origin: center top;
+  }
+}
+
+.zoomOutUp {
+  -webkit-animation-name: zoomOutUp;
+  animation-name: zoomOutUp;
+}

Разница между файлами не показана из-за своего большого размера
+ 4 - 0
frontend/saas-portal-web/src/css/bootstrap.min.css


Разница между файлами не показана из-за своего большого размера
+ 13 - 0
frontend/saas-portal-web/src/css/ionicons.min.css


+ 274 - 0
frontend/saas-portal-web/src/css/jquery.fancybox.css

@@ -0,0 +1,274 @@
+/*! fancyBox v2.1.5 fancyapps.com | fancyapps.com/fancybox/#license */
+.fancybox-wrap,
+.fancybox-skin,
+.fancybox-outer,
+.fancybox-inner,
+.fancybox-image,
+.fancybox-wrap iframe,
+.fancybox-wrap object,
+.fancybox-nav,
+.fancybox-nav span,
+.fancybox-tmp
+{
+	padding: 0;
+	margin: 0;
+	border: 0;
+	outline: none;
+	vertical-align: top;
+}
+
+.fancybox-wrap {
+	position: absolute;
+	top: 0;
+	left: 0;
+	z-index: 8020;
+}
+
+.fancybox-skin {
+	position: relative;
+	background: #f9f9f9;
+	color: #444;
+	text-shadow: none;
+	-webkit-border-radius: 4px;
+	   -moz-border-radius: 4px;
+	        border-radius: 4px;
+}
+
+.fancybox-opened {
+	z-index: 8030;
+}
+
+.fancybox-opened .fancybox-skin {
+	-webkit-box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5);
+	   -moz-box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5);
+	        box-shadow: 0 10px 25px rgba(0, 0, 0, 0.5);
+}
+
+.fancybox-outer, .fancybox-inner {
+	position: relative;
+}
+
+.fancybox-inner {
+	overflow: hidden;
+}
+
+.fancybox-type-iframe .fancybox-inner {
+	-webkit-overflow-scrolling: touch;
+}
+
+.fancybox-error {
+	color: #444;
+	font: 14px/20px "Helvetica Neue",Helvetica,Arial,sans-serif;
+	margin: 0;
+	padding: 15px;
+	white-space: nowrap;
+}
+
+.fancybox-image, .fancybox-iframe {
+	display: block;
+	width: 100%;
+	height: 100%;
+}
+
+.fancybox-image {
+	max-width: 100%;
+	max-height: 100%;
+}
+
+#fancybox-loading, .fancybox-close, .fancybox-prev span, .fancybox-next span {
+	background-image: url('fancybox_sprite.png');
+}
+
+#fancybox-loading {
+	position: fixed;
+	top: 50%;
+	left: 50%;
+	margin-top: -22px;
+	margin-left: -22px;
+	background-position: 0 -108px;
+	opacity: 0.8;
+	cursor: pointer;
+	z-index: 8060;
+}
+
+#fancybox-loading div {
+	width: 44px;
+	height: 44px;
+	background: url('fancybox_loading.gif') center center no-repeat;
+}
+
+.fancybox-close {
+	position: absolute;
+	top: -18px;
+	right: -18px;
+	width: 36px;
+	height: 36px;
+	cursor: pointer;
+	z-index: 8040;
+}
+
+.fancybox-nav {
+	position: absolute;
+	top: 0;
+	width: 40%;
+	height: 100%;
+	cursor: pointer;
+	text-decoration: none;
+	background: transparent url('blank.gif'); /* helps IE */
+	-webkit-tap-highlight-color: rgba(0,0,0,0);
+	z-index: 8040;
+}
+
+.fancybox-prev {
+	left: 0;
+}
+
+.fancybox-next {
+	right: 0;
+}
+
+.fancybox-nav span {
+	position: absolute;
+	top: 50%;
+	width: 36px;
+	height: 34px;
+	margin-top: -18px;
+	cursor: pointer;
+	z-index: 8040;
+	visibility: hidden;
+}
+
+.fancybox-prev span {
+	left: 10px;
+	background-position: 0 -36px;
+}
+
+.fancybox-next span {
+	right: 10px;
+	background-position: 0 -72px;
+}
+
+.fancybox-nav:hover span {
+	visibility: visible;
+}
+
+.fancybox-tmp {
+	position: absolute;
+	top: -99999px;
+	left: -99999px;
+	visibility: hidden;
+	max-width: 99999px;
+	max-height: 99999px;
+	overflow: visible !important;
+}
+
+/* Overlay helper */
+
+.fancybox-lock {
+    overflow: hidden !important;
+    width: auto;
+}
+
+.fancybox-lock body {
+    overflow: hidden !important;
+}
+
+.fancybox-lock-test {
+    overflow-y: hidden !important;
+}
+
+.fancybox-overlay {
+	position: absolute;
+	top: 0;
+	left: 0;
+	overflow: hidden;
+	display: none;
+	z-index: 8010;
+	background: url('fancybox_overlay.png');
+}
+
+.fancybox-overlay-fixed {
+	position: fixed;
+	bottom: 0;
+	right: 0;
+}
+
+.fancybox-lock .fancybox-overlay {
+	overflow: auto;
+	overflow-y: scroll;
+}
+
+/* Title helper */
+
+.fancybox-title {
+	visibility: hidden;
+	font: normal 13px/20px "Helvetica Neue",Helvetica,Arial,sans-serif;
+	position: relative;
+	text-shadow: none;
+	z-index: 8050;
+}
+
+.fancybox-opened .fancybox-title {
+	visibility: visible;
+}
+
+.fancybox-title-float-wrap {
+	position: absolute;
+	bottom: 0;
+	right: 50%;
+	margin-bottom: -35px;
+	z-index: 8050;
+	text-align: center;
+}
+
+.fancybox-title-float-wrap .child {
+	display: inline-block;
+	margin-right: -100%;
+	padding: 2px 20px;
+	background: transparent; /* Fallback for web browsers that doesn't support RGBa */
+	background: rgba(0, 0, 0, 0.8);
+	-webkit-border-radius: 15px;
+	   -moz-border-radius: 15px;
+	        border-radius: 15px;
+	text-shadow: 0 1px 2px #222;
+	color: #FFF;
+	font-weight: bold;
+	line-height: 24px;
+	white-space: nowrap;
+}
+
+.fancybox-title-outside-wrap {
+	position: relative;
+	margin-top: 10px;
+	color: #fff;
+}
+
+.fancybox-title-inside-wrap {
+	padding-top: 10px;
+}
+
+.fancybox-title-over-wrap {
+	position: absolute;
+	bottom: 0;
+	left: 0;
+	color: #fff;
+	padding: 10px;
+	background: #000;
+	background: rgba(0, 0, 0, .8);
+}
+
+/*Retina graphics!*/
+@media only screen and (-webkit-min-device-pixel-ratio: 1.5),
+	   only screen and (min--moz-device-pixel-ratio: 1.5),
+	   only screen and (min-device-pixel-ratio: 1.5){
+
+	#fancybox-loading, .fancybox-close, .fancybox-prev span, .fancybox-next span {
+		background-image: url('fancybox_sprite@2x.png');
+		background-size: 44px 152px; /*The size of the normal image, half the size of the hi-res image*/
+	}
+
+	#fancybox-loading div {
+		background-image: url('fancybox_loading@2x.gif');
+		background-size: 24px 24px; /*The size of the normal image, half the size of the hi-res image*/
+	}
+}

+ 1733 - 0
frontend/saas-portal-web/src/css/main.css

@@ -0,0 +1,1733 @@
+/**
+*
+* ---------------------------------------------------------------------------
+*
+* Template : Blue - A One-Page HTML Portfolio/Business Template
+* Author : Muhammad Morshd
+* Author URI : http://morshed.im
+*
+* --------------------------------------------------------------------------- 
+*
+*/
+
+/* =================================== */
+/*	Basic Style 
+/* =================================== */
+
+body {
+    background-color: #fff;
+    font-family: 'Open Sans', sans-serif;
+    line-height: 24px;
+    font-size: 16px;
+    color: #818181;
+}
+
+figure, p, address {
+    margin: 0;
+}
+iframe {
+    border: 0;
+}
+
+a {
+    color: #0aa6bd;
+    -webkit-transition: all 0.3s ease-out 0s;
+       -moz-transition: all 0.3s ease-out 0s;
+        -ms-transition: all 0.3s ease-out 0s;
+         -o-transition: all 0.3s ease-out 0s;
+            transition: all 0.3s ease-out 0s;
+}
+
+a, a:hover, a:focus, .btn:focus {
+    text-decoration: none;
+    outline: none;
+}
+
+h1, h2, h3, h4, h5, h6,
+.h1, .h2, .h3, .h4, .h5, .h6 {
+    font-family: 'Open Sans', sans-serif;
+    color: #252525;
+}
+p {
+    font-size: 15px;
+}
+main > section {
+    padding: 70px 0;
+}
+
+.btn {
+    border-radius: 0;
+    border: 0;
+    position: relative;
+    text-transform: uppercase;
+}
+
+.btn-blue {
+    background-color: rgba(10, 166, 189, 0.75);
+    box-shadow: 0 -2px 0 rgba(0, 0, 0, 0.15) inset;
+    padding: 15px 55px;
+    color: #fff;
+}
+
+.btn-border {
+    border: 2px solid #fff;
+    color: #fff;
+    padding: 12px 35px;
+}
+
+.bg-blue {
+    background-color: #0aa6bd;
+}
+
+/* Sweep To Right */
+.btn-effect {
+	vertical-align: middle;
+	box-shadow: 0 0 1px rgba(0, 0, 0, 0);
+	position: relative;
+	display: inline-block;
+
+	-webkit-transform: translateZ(0);
+	   -moz-transform: translateZ(0);
+		-ms-transform: translateZ(0);
+		 -o-transform: translateZ(0);
+			transform: translateZ(0);
+		  
+	-webkit-backface-visibility: hidden;
+	   -moz-backface-visibility: hidden;
+			backface-visibility: hidden;
+		  
+	-webkit-transition-property: color;
+	   -moz-transition-property: color;
+		-ms-transition-property: color;
+			transition-property: color;
+		  
+	-webkit-transition-duration: 0.3s;
+	   -moz-transition-duration: 0.3s;
+		-ms-transition-duration: 0.3s;
+			transition-duration: 0.3s;
+		  
+	-moz-osx-font-smoothing: grayscale;
+}
+
+.btn-effect:before {
+	content: "";
+	position: absolute;
+	z-index: -1;
+	top: 0;
+	left: 0;
+	right: 0;
+	bottom: 0;
+	background: #fff;
+	
+	-webkit-transform: scaleX(0);
+	   -moz-transform: scaleX(0);
+		-ms-transform: scaleX(0);
+			transform: scaleX(0);
+			
+	-webkit-transform-origin: 0 50%;
+	   -moz-transform-origin: 0 50%;
+		-ms-transform-origin: 0 50%;
+			transform-origin: 0 50%;
+			
+	-webkit-transition-property: transform;
+	   -moz-transition-property: transform;
+		-ms-transition-property: transform;
+			transition-property: transform;
+			
+	-webkit-transition-duration: 0.3s;
+	   -moz-transition-duration: 0.3s;
+		-ms-transition-duration: 0.3s;
+			transition-duration: 0.3s;
+			
+	-webkit-transition-timing-function: ease-out;
+	   -moz-transition-timing-function: ease-out;
+		-ms-transition-timing-function: ease-out;
+			transition-timing-function: ease-out;
+}
+
+.btn-effect:hover, .btn-effect:focus, .btn-effect:active {
+	color: rgb(9, 157, 178);
+}
+
+.btn-effect:hover:before, .btn-effect:focus:before, .btn-effect:active:before {
+	-webkit-transform: scaleX(1);
+	   -moz-transform: scaleX(1);
+	    -ms-transform: scaleX(1);
+			transform: scaleX(1);
+}
+
+.section-title {
+    margin-bottom: 80px;
+}
+
+.section-title.white {
+    color: #fff;
+}
+
+.section-title h2 {
+    color: #0aa6bd;
+    font-size: 24px;
+    font-weight: 700;
+    line-height: 20px;
+    margin-bottom: 25px;
+    padding-bottom: 20px;
+    position: relative;
+    text-transform: uppercase;
+}
+.section-title p {
+    color: #444;
+    font-style: italic;
+    font-size: 13px;
+}
+.section-title.white p {
+    color: #fff;
+}
+
+.section-title.white h2 {
+    color: #fff;
+}
+
+.section-title h2:after {
+    position: absolute;
+    left: 50%;
+    bottom: 0;
+    height: 2px;
+    width: 72px;
+    margin-left: -36px;
+    background: #636363;
+    content: "";
+}
+
+.section-title.white h2:after {
+    background: #fff;
+}
+
+.parallax {
+    background-attachment: fixed;
+    background-position: center top;
+    background-repeat: no-repeat;
+    background-size: cover;
+}
+
+
+/**
+/*	Preloader
+/* ==========================================*/
+
+#preloader {
+    background-color: #fff;
+    position: fixed;
+    width: 100%;
+    height: 100%;
+    z-index: 9999;
+}
+
+/*Battery*/
+.loder-box {
+  background-color: rgba(0, 0, 0, 0.02);
+  border-radius: 1px;
+  height: 100px;
+  left: 50%;
+  margin-left: -64px;
+  margin-top: -50px;
+  position: absolute;
+  top: 50%;
+  width: 128px;
+}
+
+.battery{
+    width: 60px;
+    height: 25px;
+    top: 35%;
+    border: 1px #2E2E2E solid;
+    border-radius: 2px;
+    position: relative;
+    -webkit-animation: charge 5s linear infinite;
+       -moz-animation: charge 5s linear infinite;
+            animation: charge 5s linear infinite;
+    margin: 0 auto;
+}
+
+.battery:after {
+  background-color: #2E2E2E;
+  border-radius: 0 1px 1px 0;
+  content: "";
+  height: 10px;
+  position: absolute;
+  right: -5px;
+  top: 7px;
+  width: 3px;
+}
+
+@-webkit-keyframes charge{
+    0%{box-shadow: inset 0px 0px 0px #2E2E2E;}
+    100%{box-shadow: inset 60px 0px 0px #2E2E2E;}
+}
+
+@-moz-keyframes charge{
+    0%{box-shadow: inset 0px 0px 0px #2E2E2E;}
+    100%{box-shadow: inset 60px 0px 0px #2E2E2E;}
+}
+
+@keyframes charge{
+    0%{box-shadow: inset 0px 0px 0px #2E2E2E;}
+    100%{box-shadow: inset 60px 0px 0px #2E2E2E;}
+}
+
+
+
+
+
+/**
+/*	Header
+/* ==========================================*/
+
+#navigation {
+    -webkit-transition: all 0.8s ease 0s;
+       -moz-transition: all 0.8s ease 0s;
+        -ms-transition: all 0.8s ease 0s;
+         -o-transition: all 0.8s ease 0s;
+            transition: all 0.8s ease 0s;
+
+    background-color: rgba(0, 0, 0, 0.77);
+    box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
+    padding: 5px 0;
+}
+
+#navigation.animated-header {
+    padding: 20px 0;
+}
+
+h1.navbar-brand {
+    font-size: 20px;
+    font-weight: 700;
+    margin: 0;
+    text-transform: uppercase;
+}
+
+.navbar-inverse .navbar-nav > li > a {
+    color: #fff;
+    font-size: 13px;
+    text-transform: uppercase;
+    font-weight: bold;
+}
+.menu {
+
+}
+.menu li a {
+    display: inline-block;
+}
+.menu li a span {
+
+}
+.menu li a span:before {
+    content: "";
+    position: absolute;
+    width: 100%;
+    height: 2px;
+    bottom: 0;
+    left: 0;
+    background-color: #0aa6bd;
+    visibility: hidden;
+    -webkit-transform: scaleX(0);
+    -moz-transform: scaleX(0);
+    -ms-transform: scaleX(0);
+    -o-transform: scaleX(0);
+    transform: scaleX(0);
+    -webkit-transition: all 0.3s ease-in-out 0s;
+    -moz-transition: all 0.3s ease-in-out 0s;
+    -ms-transition: all 0.3s ease-in-out 0s;
+    -o-transition: all 0.3s ease-in-out 0s;
+    transition: all 0.3s ease-in-out 0s;
+}
+.menu li a:hover span:before {
+    visibility: visible;
+    -webkit-transform: scaleX(1);
+    -moz-transform: scaleX(1);
+    -ms-transform: scaleX(1);
+    -o-transform: scaleX(1);
+    transform: scaleX(1);
+}
+
+/*=================================================================
+	Home Slider
+==================================================================*/
+
+
+#home-slider {
+    position: relative;
+    padding: 0;
+}
+.mask-overly {
+    background: rgba(0, 0, 0, 0.2) none repeat scroll 0 0;
+    bottom: 0;
+    left: 0;
+    position: absolute;
+    right: 0;
+    top: 0;
+    z-index: 9;
+
+}
+#slider #nav-arrows > a {
+    font-size: 40px;
+    line-height: 1.5;
+}
+.slider-1 {
+    background-image: url(../img/slider/slider-1.jpg);
+}
+.slider-2 {
+    background-image: url(../img/slider/slider-2.jpg);
+}
+.slider-3 {
+    background-image: url(../img/slider/slider-3.jpg);
+}
+.sl-slider-wrapper {
+    width: 100%;
+    margin: 0 auto;
+    position: relative;
+    overflow: hidden;
+}
+
+.sl-slider {
+    position: absolute;
+    top: 0;
+    left: 0;
+}
+
+/* Slide wrapper and slides */
+
+.sl-slide,
+.sl-slides-wrapper,
+.sl-slide-inner {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    top: 0;
+    left: 0;
+} 
+
+.sl-slide {
+    z-index: 1;
+}
+
+.slide-caption {
+    color: #fff;
+    display: table;
+    height: 100%;
+    left: 0;
+    min-height: 100%;
+    position: absolute;
+    text-align: center;
+    top: 0;
+    width: 60%;
+    z-index: 999999;
+    left: 50%;
+    transform: translateX(-50%);
+
+}
+
+.slide-caption .caption-content {
+    vertical-align: middle;
+    display: table-cell;
+}
+
+.caption-content h2 {
+    color: #fff;
+    font-size: 50px;
+    font-weight: 900;
+    margin-bottom: 25px;
+}
+
+.caption-content > p {
+    display: block;
+    font-size: 16px;
+    margin-bottom: 45px;
+    text-transform: capitalize;
+    margin-bottom: 65px;
+    font-weight: 200;
+}
+
+/*Slider Arrow Buttons*/
+#nav-arrows > a {
+    border: 1px solid #fff;
+    color: #fff;
+    display: block;
+    height: 60px;
+    line-height: 76px;
+    position: absolute;
+    text-align: center;
+    top: 50%;
+    width: 60px;
+    z-index: 20;
+    margin-top: -30px;
+    -webkit-transition: all 0.3s ease 0s;
+    -moz-transition: all 0.3s ease 0s;
+    -ms-transition: all 0.3s ease 0s;
+    -o-transition: all 0.3s ease 0s;
+    transition: all 0.3s ease 0s;
+}
+
+#nav-arrows > a.sl-prev {
+
+    transform: translateX(-50px);
+    opacity:0;
+}
+#slider:hover #nav-arrows > a.sl-prev {
+    opacity: 1;
+    transform: translateX(20px);
+}
+#nav-arrows > a.sl-next {
+    right:0;
+    transform: translateX(50px);
+    opacity:0;
+}
+#slider:hover #nav-arrows > a.sl-next {
+    opacity: 1;
+    transform: translateX(-20px);
+}
+#nav-arrows > a.sl-next:hover ,#nav-arrows > a.sl-prev:hover {
+    background-color: #0aa6bd;
+    border-color: #0aa6bd;
+    color: #fff;
+}
+
+
+/* The duplicate parts/slices */
+
+.sl-content-slice {
+    overflow: hidden;
+    position: absolute;
+    -webkit-box-sizing: content-box;
+    -moz-box-sizing: content-box;
+    box-sizing: content-box;
+    background: #fff;
+    -webkit-backface-visibility: hidden;
+    -moz-backface-visibility: hidden;
+    -o-backface-visibility: hidden;
+    -ms-backface-visibility: hidden;
+    backface-visibility: hidden;
+    opacity : 1;
+}
+
+/* Horizontal slice */
+
+.sl-slide-horizontal .sl-content-slice {
+    width: 100%;
+    height: 50%;
+    left: -200px;
+    -webkit-transform: translateY(0%) scale(1);
+    -moz-transform: translateY(0%) scale(1);
+    -o-transform: translateY(0%) scale(1);
+    -ms-transform: translateY(0%) scale(1);
+    transform: translateY(0%) scale(1);
+}
+
+.sl-slide-horizontal .sl-content-slice:first-child {
+    top: -200px;
+    padding: 200px 200px 0px 200px;
+}
+
+.sl-slide-horizontal .sl-content-slice:nth-child(2) {
+    top: 50%;
+    padding: 0px 200px 200px 200px;
+}
+
+/* Vertical slice */
+
+.sl-slide-vertical .sl-content-slice {
+    width: 50%;
+    height: 100%;
+    top: -200px;
+    -webkit-transform: translateX(0%) scale(1);
+    -moz-transform: translateX(0%) scale(1);
+    -o-transform: translateX(0%) scale(1);
+    -ms-transform: translateX(0%) scale(1);
+    transform: translateX(0%) scale(1);
+}
+
+.sl-slide-vertical .sl-content-slice:first-child {
+    left: -200px;
+    padding: 200px 0px 200px 200px;
+}
+
+.sl-slide-vertical .sl-content-slice:nth-child(2) {
+    left: 50%;
+    padding: 200px 200px 200px 0px;
+}
+
+/* Content wrapper */
+/* Width and height is set dynamically */
+.sl-content-wrapper {
+    position: absolute;
+}
+
+.sl-content {
+    width: 100%;
+    height: 100%;
+}
+
+
+
+/* Project laughtbox setup */
+
+.fancybox-item.fancybox-close {
+    background: url("../img/icons/close.png") no-repeat scroll 0 0 transparent;
+    height: 50px;
+    right: 0;
+    top: 0;
+    width: 50px;
+}
+
+.fancybox-next span {
+    background: url("../img/right.png") no-repeat scroll center center #0aa6bd;
+    height: 50px;
+    width: 50px;
+    right: 0;
+}
+
+.fancybox-prev span {
+    background: url("../img/left.png") no-repeat scroll center center #0aa6bd;
+    height: 50px;
+    width: 50px;
+    left: 0;
+}
+
+/*=================================================================
+    Feature
+==================================================================*/
+
+.list-nav {
+    padding:0;
+    margin-top: 20px;
+}
+.list-nav li {
+    list-style: none;
+    margin: 0;
+    padding: 0;
+    font-size: 16px;
+    padding-left: 30px;
+    margin-bottom: 10px;
+    position: relative;
+}
+.list-nav li i {
+    position: absolute;
+    left: 0;
+    top: 4px;
+    font-size: 28px;
+    color: #1fb5f6;
+}
+
+
+
+
+/*=================================================================
+    Prototype
+==================================================================*/
+
+#prototype {
+    background: #f1f1f1;
+}
+#prototype img {
+}
+#prototype .block {
+    
+}
+#prototype .block img {
+    width: 100%;
+    height: auto;
+    margin-bottom: 20px;
+}
+#prototype .block p {
+    margin-top: 10px;
+}
+#prototype .block .btn {
+    margin-top: 20px;
+}
+
+
+
+
+
+
+
+
+
+
+/*=================================================================
+	Services
+==================================================================*/
+
+.service-icon {
+    border: 3px solid transparent;
+    display: inline-block;
+    line-height: 40px;
+    -webkit-transition: all 0.3s ease 0s;
+       -moz-transition: all 0.3s ease 0s;
+        -ms-transition: all 0.3s ease 0s;
+         -o-transition: all 0.3s ease 0s;
+            transition: all 0.3s ease 0s;
+}
+.service-item {
+    margin-bottom: 60px;
+    padding:0 14px;
+}
+.service-icon i {
+    font-size: 60px;
+    color: #0aa6bd;
+}
+
+.service-item h3 {
+    font-size: 13px;
+    font-weight: 600;
+    margin-top: 10px;
+    margin-bottom: 20px;
+    color: #3c3d41;
+    text-transform: uppercase;
+}
+.service-item p {
+    color: #7e848e;
+    font-weight: 200;
+}
+
+
+
+
+/*=================================================================
+    Video Section
+==================================================================*/
+
+
+.video-bg {
+    background: transparent url("../img/video-bg.jpg") no-repeat scroll center center / cover ;
+    color: #fff;
+    position: relative;
+    text-align: center;
+    z-index: 1;
+}
+
+.modal-section {
+
+}
+.modal-dialog {
+    margin:100px auto;
+}
+.modal-header {
+    border:none;
+}
+
+.video-bg .section-title p {
+    color: #fff;
+}
+
+.video-bg .overlay {
+    background-color: rgba(0, 0, 0, 0.5);
+    position: absolute;
+    left: 0;
+    right: 0;
+    top: 0;
+    bottom: 0;
+    z-index: 0;
+}
+
+.video-popup-button {
+    background-color: #0aa6bd;
+    border-radius: 50%;
+    color: #fff;
+    display: inline-block;
+    font-size: 90px;
+    height: 150px;
+    line-height: 150px;
+    margin-bottom: 80px;
+    width: 150px;
+    position: relative;
+}
+.video-popup-button i {
+    margin-left: 15px;
+}
+
+.video-popup-button:hover,.video-popup-button:focus {
+    background-color: transparent;
+    color: #fff;
+}
+
+.video-popup-button:before {
+    border: 2px solid #fff;
+    border-radius: 50%;
+    bottom: 0;
+    content: "";
+    left: 0;
+    opacity: 0;
+    position: absolute;
+    right: 0;
+    top: 0;
+    transition: all 0.3s ease 0s;
+}
+
+.video-popup-button:hover:before {
+    opacity: 1;
+}
+
+/*=================================================================
+	Portfolio
+==================================================================*/
+
+.project-wrapper {
+    margin: 0;
+    padding: 0;
+    list-style: none;
+    text-align: center;
+}
+
+.project-wrapper .row {
+    margin: 0;
+}
+
+.project-wrapper [class^="col-"] {
+    padding-left: 0;
+    padding-right: 0;
+}
+
+.portfolio-filter {
+    font-size: 0;
+    list-style: outside none none;
+    margin: 0 0 40px;
+    padding: 0;
+}
+.portfolio-filter li {
+    display: inline-block;
+}
+.portfolio-filter li a {
+    color: #777;
+    display: block;
+    font-size: 14px;
+    padding: 0 20px;
+    position: relative;
+}
+.portfolio-filter li a.active {
+    color: #0aa6bd;
+}
+.portfolio-filter li a::after {
+    bottom: auto;
+    content: "/";
+    position: absolute;
+    right: 0;
+    top: auto;
+}
+.portfolio-filter li:last-child a::after {
+    content: none;
+}
+
+
+.portfolio-item {
+    cursor: pointer;
+    overflow: hidden;
+    position: relative;
+}
+
+.portfolio-item img {
+    -webkit-transition: all 0.4s ease 0s;
+       -moz-transition: all 0.4s ease 0s;
+         -o-transition: all 0.4s ease 0s;
+            transition: all 0.4s ease 0s;
+}
+
+.portfolio-item:hover img {
+    -webkit-transform: translateY(-105px);
+    -ms-transform: translateY(-105px);
+    -o-transform: translateY(-105px);
+    transform: translateY(-105px);
+}
+
+figcaption.mask {
+    background-color: #f1f1f1;
+    bottom: 0;
+    color: #333;
+    padding: 25px;
+    position: absolute;
+    width: 100%;
+    text-align: left;
+    -webkit-transform: translateY(100%);
+    -ms-transform: translateY(100%);
+    -o-transform: translateY(100%);
+    transform: translateY(100%);
+    -webkit-transition: all 0.4s ease 0s;
+       -moz-transition: all 0.4s ease 0s;
+         -o-transition: all 0.4s ease 0s;
+            transition: all 0.4s ease 0s;
+}
+
+.portfolio-item:hover figcaption.mask {
+    -webkit-transform: translateY(0px);
+    -ms-transform: translateY(0px);
+    -o-transform: translateY(0px);
+    transform: translateY(0px);
+}
+
+figcaption.mask h3 {
+    margin-top: 0;
+    color: #333;
+    font-size: 18px;
+    margin-bottom:15px;
+    opacity: 0;
+    -webkit-transform: translateY(30px);
+    -ms-transform: translateY(30px);
+    -o-transform: translateY(30px);
+    transform: translateY(30px);
+    -webkit-transition: all 0.4s ease 0.1s;
+       -moz-transition: all 0.4s ease 0.1s;
+         -o-transition: all 0.4s ease 0.1s;
+            transition: all 0.4s ease 0.1s;
+}
+figcaption.mask p {
+    line-height: 1.3;
+    -webkit-transform: translateY(30px);
+    -ms-transform: translateY(30px);
+    -o-transform: translateY(30px);
+    transform: translateY(30px);
+    opacity: 0;
+    -webkit-transition: all 0.4s ease 0.3s;
+       -moz-transition: all 0.4s ease 0.3s;
+         -o-transition: all 0.4s ease 0.3s;
+            transition: all 0.4s ease 0.3s;
+}
+
+.portfolio-item:hover figcaption.mask h3,
+.portfolio-item:hover figcaption.mask p {
+    opacity: 1;
+    -webkit-transform: translateY(0px);
+    -ms-transform: translateY(0px);
+    -o-transform: translateY(0px);
+    transform: translateY(0px);
+}
+
+ul.external {
+    list-style: outside none none;
+    margin: 0;
+    padding: 0;
+    position: absolute;
+    right: 0;
+    top: -47px;
+    
+    -webkit-transition: all 0.4s ease 0s;
+       -moz-transition: all 0.4s ease 0s;
+        -ms-transition: all 0.4s ease 0s;
+         -o-transition: all 0.4s ease 0s;
+            transition: all 0.4s ease 0s;
+}
+
+ul.external li {
+    display: inline-block;
+}
+
+ul.external li a {
+    background-color: rgba(255, 255, 255, 0.9);
+    color: #818181;
+    display: block;
+    padding: 10px 18px 13px;
+    
+    -webkit-transition: all 0.5s ease 0s;
+       -moz-transition: all 0.5s ease 0s;
+        -ms-transition: all 0.5s ease 0s;
+         -o-transition: all 0.5s ease 0s;
+            transition: all 0.5s ease 0s;
+}
+
+ul.external li a:hover {
+    background-color: #0aa6bd;
+    color: #fff;
+}
+
+.portfolio-item:hover ul.external {
+    top: 0;
+}
+
+.fancybox-skin {
+    border-radius: 0;
+}
+
+.fancybox-title.fancybox-title-inside-wrap {
+    padding: 15px;
+}
+
+.fancybox-title h3 {
+    margin: 0 0 15px;
+}
+
+.fancybox-title p {
+    color: #818181;
+    font-size: 16px;
+    line-height: 22px;
+}
+
+.fancybox-title-inside-wrap {
+    padding-top: 0;
+}
+
+/*=================================================================
+	Testimonials
+==================================================================*/
+
+
+#testimonials {
+    background-image: url(../img/parallax/testimonial.jpg);
+    /*padding: 0;*/
+    color: #fff;
+    position: relative;
+}
+
+
+#testimonials:before  {
+    background-color: rgba(10, 166, 189, 0.75);
+    padding: 70px 0 40px;
+    position: absolute;
+    top: 0;
+    right: 0;
+    left: 0;
+    bottom: 0;
+    content: '';
+}
+
+.testimonial-item {
+    margin: 0 auto;
+    padding-bottom: 50px;
+    width: 64%;
+}
+
+.testimonial-item img {
+    border: 3px solid #fff;
+    border-radius: 50%;
+    display: inline-block;
+    height: auto;
+    max-width: 100px;
+}
+
+.testimonial-item > div {
+    line-height: 30px;
+    position: relative;
+}
+
+.testimonial-item > div:before {
+    background-image: url("../img/icons/quotes.png");
+    background-repeat: no-repeat;
+    bottom: 127px;
+    height: 33px;
+    left: -35px;
+    position: absolute;
+    width: 45px;
+}
+
+.testimonial-item > div:after {
+    background-image: url("../img/icons/quotes.png");
+    background-position: -58px 0;
+    background-repeat: no-repeat;
+    bottom: -50px;
+    height: 33px;
+    position: absolute;
+    right: 0;
+    width: 45px;
+}
+
+.testimonial-item > div > span {
+    display: inline-block;
+    font-weight: 700;
+    margin: 40px 0 30px;
+    text-transform: uppercase;
+}
+
+#testimonials .owl-controls.clickable {
+    text-align: center;
+}
+
+#testimonials .owl-buttons {
+    border: 2px solid #fff;
+    float: inherit;
+    border: 2px solid #fff;
+    display: inline-block;
+    padding: 0px 0px 0px;
+}
+
+.owl-controls .owl-page, .owl-controls .owl-buttons div {
+    cursor: pointer;
+    display: inline-block;
+    margin: 0 8px;
+    padding: 10px;
+}
+
+
+#testimonials .owl-prev:hover,
+#testimonials .owl-next:hover {
+    color: #fff;
+}
+
+.price-table {
+    border: 1px solid #e3e3e3;
+}
+
+.price-table.featured {
+    border-color: #e3e3e3;
+    border-style: solid;
+    border-width: 1px 1px 0 1px;
+    -webkit-box-shadow: 0 4px 5px rgba(0,0,0,0.19);
+       -moz-box-shadow: 0 4px 5px rgba(0,0,0,0.19);
+            box-shadow: 0 4px 5px rgba(0,0,0,0.19);
+}
+
+.price-table > span {
+    color: #444;
+    display: block;
+    font-size: 24px;
+    padding: 30px 0;
+    text-transform: uppercase;
+    font-weight: bold;
+    letter-spacing: 2px;
+}
+
+.price-table .value {
+    background-color: #f8f8f8;
+    color: #727272;
+    padding: 20px 0;
+
+    -webkit-transition: all 0.7s ease 0s;
+       -moz-transition: all 0.7s ease 0s;
+        -ms-transition: all 0.7s ease 0s;
+         -o-transition: all 0.7s ease 0s;
+            transition: all 0.7s ease 0s;
+}
+
+.price-table.featured .value {
+    background-color: #0aa6bd;
+    color: #fff;
+}
+
+.price-table .value span {
+    display: inline-block;
+}
+
+.price-table .value span:first-child {
+    font-size: 32px;
+    line-height: 32px;
+}
+
+.price-table .value span:nth-child(2) {
+    font-size: 40px;
+    line-height: 40px;
+    margin-bottom: 10px;
+}
+
+.price-table .value span:last-child {
+    font-size: 16px;
+}
+
+.price-table ul {
+    margin: 0;
+    padding: 0;
+    list-style: none;
+    text-align: center;
+}
+
+.price-table ul li {
+    border-top: 1px solid #e3e3e3;
+    display: block;
+    padding: 15px 0;
+
+    -webkit-transition: all 0.7s ease 0s;
+       -moz-transition: all 0.7s ease 0s;
+        -ms-transition: all 0.7s ease 0s;
+         -o-transition: all 0.7s ease 0s;
+            transition: all 0.7s ease 0s;
+}
+
+.price-table ul li a {
+    display: block;
+    text-transform: uppercase;
+}
+
+.price-table.featured ul li:last-child,
+.price-table ul li:last-child:hover {
+    background-color: #0aa6bd;
+}
+
+.price-table.featured ul li:last-child a,
+.price-table ul li:last-child:hover a {
+    color: #fff;
+}
+
+
+/*=================================================================
+	Price
+==================================================================*/
+
+#social {
+    background-image: url(../img/parallax/testimonial.jpg);
+    padding: 0;
+}
+
+#social .overlay {
+    background: url("../img/slide_bg.png") repeat scroll 0 0 transparent;
+    padding: 100px 0 120px;
+}
+
+.social-button {
+    margin: 0;
+    padding: 0;
+    list-style: none;
+    text-align: center;
+}
+
+.social-button li {
+    display: inline-block;
+    margin:0 20px;
+}
+
+
+.social-button li a {
+    border: 2px solid #fff;
+    border-radius: 50%;
+    color: #fff;
+    display: block;
+    height: 90px;
+    line-height: 96px;
+    width: 90px;
+    font-size: 40px;
+    line-height: 90px;
+    -webkit-transition: all 0.2s ease 0s;
+       -moz-transition: all 0.2s ease 0s;
+        -ms-transition: all 0.2s ease 0s;
+         -o-transition: all 0.2s ease 0s;
+            transition: all 0.2s ease 0s;
+}
+
+.social-button li a:hover {
+    border: 2px solid #0aa6bd;
+    background: #0aa6bd;
+    color: #fff;
+}
+
+
+/*=================================================================
+	Contact
+==================================================================*/
+
+.input-field {
+    margin-bottom: 10px;
+}
+
+.form-control {
+    border: 1px solid #ececec;
+    border-radius: 0;
+    box-shadow: none;
+    color: #818181;
+    font-size: 14px;
+    height: 40px;
+
+}
+.form-control:focus {
+    box-shadow: none;
+    border-color: #0aa6bd;
+}
+
+textarea.form-control {
+    width: 100%;
+    height: 165px;
+}
+
+.input-field label.error {
+    color: red;
+    display: block;
+    font-size: 13px;
+    font-weight: 400;
+}
+
+#submit:hover {
+  color: #fff;
+}
+
+#submit:before {
+  background-color: rgba(10, 166, 189, 0.75);
+}
+
+#submit.btn-effect:after {
+  background: #2E2E2E;
+}
+
+.contact-details h3 {
+    border-bottom: 1px solid #dedede;
+    font-weight: 600;
+    margin-bottom: 15px;
+    padding-bottom: 15px;
+    text-transform: uppercase;
+    font-size: 16px;
+    margin-top: 0;
+    color: #666;
+}
+
+.contact-details p {
+    line-height: 30px;
+}
+
+.contact-details p i {
+    margin-right: 15px;
+    font-size: 25px;
+    color: #000;
+}
+
+.contact-details span {
+    display: block;
+    margin-left: 24px;
+}
+
+
+/*============================================================
+	Google Maps
+==============================================================*/
+
+#google-map {
+    padding: 0;
+}
+
+#map-canvas {
+    width: 100%;
+    height: 300px;
+}
+
+
+/*============================================================
+	Footer
+==============================================================*/
+
+#footer {
+    background-color: #2E2E2E;
+    padding: 70px 0;
+    color: #fff;
+}
+
+.footer-content {
+    width: 390px;
+    margin: 0 auto;
+}
+
+.footer-content > div {
+    margin-bottom: 40px;
+}
+
+.footer-content > div > p:first-child {
+    margin-bottom: 15px;
+    text-transform: uppercase;
+}
+
+.subscribe-form {
+    position: relative;
+}
+
+.subscribe.form-control {
+    background-color: transparent;
+    border: 1px solid #7f7f7f;
+}
+
+.subscribe.form-control:focus {
+    -webkit-box-shadow: none;
+       -moz-box-shadow: none;
+            box-shadow: none;
+}
+
+.submit-icon {
+    background-color: #7f7f7f;
+    border: 0 none;
+    border-radius: 0;
+    color: #c1c1c1;
+    padding: 8px 20px;
+    position: absolute;
+    overflow: hidden;
+    right: 0;
+    top: 0;
+}
+
+.submit-icon .fa-paper-plane {
+    position: relative;
+    top: 0;
+    
+    -webkit-transform: translateX(0);
+       -moz-transform: translateX(0);
+        -ms-transform: translateX(0);
+         -o-transform: translateX(0);
+            transform: translateX(0);
+
+    -webkit-transition: all 0.3s ease 0.2s;
+       -moz-transition: all 0.3s ease 0.2s;
+        -ms-transition: all 0.3s ease 0.2s;
+         -o-transition: all 0.3s ease 0.2s;
+            transition: all 0.3s ease 0.2s;
+}
+
+.submit-icon:hover .fa-paper-plane {
+    position: relative;
+    top: -37px;
+    
+    -webkit-transform: translateX(30px);
+       -moz-transform: translateX(30px);
+        -ms-transform: translateX(30px);
+         -o-transform: translateX(30px);
+            transform: translateX(30px);
+}
+
+.footer-content .footer-social {
+    margin: 40px 0 35px;
+}
+
+.footer-social ul {
+    list-style: outside none none;
+    margin: 0;
+    padding: 0;
+    text-align: center;
+}
+
+.footer-social ul li {
+    display: inline-block;
+    margin: 0 10px;
+}
+
+.footer-social ul li a {
+    color: #7f7f7f;
+    display: block;
+    
+    -webkit-transition: all 0.2s ease 0s;
+       -moz-transition: all 0.2s ease 0s;
+        -ms-transition: all 0.2s ease 0s;
+         -o-transition: all 0.2s ease 0s;
+            transition: all 0.2s ease 0s;
+}
+
+.footer-social ul li a:hover {
+    color: #0aa6bd;
+}
+
+.footer-content > p {
+    color: #ababab;
+    font-size: 12px;
+}
+
+
+
+/*============================================================ 
+	Responsive Styles
+ ============================================================*/
+
+/*============================================================
+	For Small Desktop
+==============================================================*/
+
+@media (min-width: 980px) and (max-width: 1150px) {
+
+/*about*/
+    #about .welcome-block img {
+        margin-bottom: 30px;
+    }
+
+}
+
+
+/*============================================================
+	Tablet (Portrait) Design for a width of 768px
+==============================================================*/
+
+@media (min-width: 768px) and (max-width: 979px) {
+
+/* home slider  */
+    .caption-content h2 {
+        font-size: 40px;
+    }
+
+    .caption-content p {
+        font-size: 25px;
+    }
+
+    .caption-content strong {
+        font-size: 45px;
+    }
+
+/* about */
+    .recent-works {
+        margin-bottom: 50px;
+    }
+
+    .service-item {
+        margin-bottom: 50px;
+    }
+/* testimonial */
+    .testimonial-item {
+        width: 100%;
+    }
+
+    .testimonial-item {
+        width: 80%;
+    }
+
+    .testimonial-item > div:after {
+        bottom: -35px;
+    }
+
+/* price */
+    .price-table {
+        margin-bottom: 50px;
+    }
+
+/* contact form */
+    .contact-form {
+        margin-bottom: 50px;
+    }
+}
+
+
+/*============================================================
+	Mobile (Portrait) Design for a width of 320px
+==============================================================*/
+
+@media only screen and (max-width: 767px) {
+
+    body {
+        font-size: 14px;
+    }
+
+    .parallax {
+        background-position: centet tip !important;
+    }
+
+    .section-title h2 {
+        font-size: 25px;
+    }
+
+    .section-title h2:after {
+        left: 30%;
+    }
+
+/*navigation*/
+    .navbar-inverse .navbar-toggle {
+        border-color: #fff;
+    }
+
+    .navbar-inverse .navbar-toggle:hover,
+    .navbar-inverse .navbar-toggle:focus {
+        background-color: transparent;
+    }
+
+/* slider */
+    .caption-content h2 {
+        font-size: 18px;
+    }
+
+    .caption-content > span {
+        font-size: 16px;
+        margin-bottom: 20px;
+    }
+
+    .caption-content p {
+        font-size: 16px;
+        margin-bottom: 30px;
+    }
+
+    .caption-content strong {
+        font-size: 22px;
+    }
+
+/* about */
+    .recent-works {
+        margin-bottom: 85px;
+    }
+
+    #about h3 {
+        font-size: 18px;
+        margin: 0 0 35px !important;
+    }
+
+    #about .owl-buttons {
+        margin-top: 20px;
+    }
+
+    #about .message-body {
+        margin-bottom: 45px;
+    }
+
+    #about .welcome-block img {
+        margin: 0 25px 15px 0;
+    }
+
+/* service */
+    .service-item {
+        width: 100%;
+    }
+
+/*portfolio*/
+
+
+/* lightbox */
+    .fancybox-title h3 {
+        font-size: 20px;
+    }
+
+    .fancybox-title p {
+        font-size: 14px;
+    }
+
+/* testimonial */
+    .testimonial-item {
+        width: 95%;
+    }
+
+    .testimonial-item > div:before,
+    .testimonial-item > div:after {
+        background-image: none;
+    }
+
+    .testimonial-item > div > span {
+        margin: 30px 0 20px;
+    }
+
+/* price */
+    .price-table {
+        margin-bottom: 50px;
+    }
+
+/* follow us */
+    
+
+    .social-button li a {
+        height: 65px;
+        line-height: 71px;
+        width: 65px;
+    }
+
+/* contact form */
+    .contact-form {
+        margin-bottom: 50px;
+    }
+
+/* footer */
+    .footer-content {
+        width: 100%;
+    }
+
+    .footer-social ul li {
+        margin: 0 7px;
+    }
+}
+
+
+/*============================================================
+	Mobile (Landscape) Design for a width of 480px
+==============================================================*/
+
+@media only screen and (min-width: 480px) and (max-width: 767px) {
+
+    .section-title h2:after {
+        left: 38%;
+    }
+
+/* home slider */
+    .caption-content h2 {
+        font-size: 35px;
+    }
+
+    .caption-content p {
+        font-size: 25px;
+    }
+
+    .caption-content strong {
+        font-size: 35px;
+    }
+
+/*about*/
+
+    #about .welcome-block h3 {
+        margin: 0 0 35px;
+    }
+
+/* service */
+
+    .service-item {
+        margin: 0 auto 50px;
+        width: 55%;
+    }
+
+/* portfolio */
+    .portfolio-item {
+        width: 48%;
+    }
+
+    figcaption.mask {
+        bottom: -132px;
+    }
+
+/* testimonial */
+    .testimonial-item {
+        width: 80%;
+    }
+
+    .testimonial-item > div:before {
+        bottom: 160px;
+    }
+
+/* social */
+   
+
+    .social-button li a {
+        height: 90px;
+        line-height: 96px;
+        width: 90px;
+    }
+
+/* price */
+    .price-table {
+        margin-bottom: 50px;
+    }
+
+/* contact form */
+    .contact-form {
+        margin-bottom: 50px;
+    }
+
+/* footer */
+    .footer-content {
+        width: 380px;
+    }
+}
+
+
+
+/*Bottom Footer*/
+#footer p {
+    color: #6f6f6f;
+}
+#footer form {
+    margin-top: 20px;
+}
+#footer ul {
+    padding-left: 0;
+}
+#footer ul li  {
+    list-style: none;
+    margin-bottom: 5px;
+}
+#footer ul li a {
+    color: #6f6f6f;
+    display: block;
+}
+#footer ul li a:hover {
+    color: rgb(9, 157, 178);
+}
+#footer h4 {
+    color: #fff;
+    text-transform: uppercase;
+    font-weight: 500;
+    margin-top: 0;
+    margin-bottom: 30px;
+    letter-spacing: 2px;
+    font-size: 16px;
+}
+
+#footer-bottom  {
+    background:#2A2A2A;
+    padding:20px 0;
+}
+#footer-bottom p {
+    font-size: 13px;
+}

+ 231 - 0
frontend/saas-portal-web/src/css/owl.carousel.css

@@ -0,0 +1,231 @@
+/* 
+ * 	Core Owl Carousel CSS File
+ *	v1.24
+ */
+
+/* clearfix */
+.owl-carousel .owl-wrapper:after {
+	content: ".";
+	display: block;
+	clear: both;
+	visibility: hidden;
+	line-height: 0;
+	height: 0;
+}
+/* display none until init */
+.owl-carousel{
+	display: none;
+	position: relative;
+	width: 100%;
+	-ms-touch-action: pan-y;
+}
+.owl-carousel .owl-wrapper{
+	display: none;
+	position: relative;
+	-webkit-transform: translate3d(0px, 0px, 0px);
+}
+.owl-carousel .owl-wrapper-outer{
+	overflow: hidden;
+	position: relative;
+	width: 100%;
+}
+.owl-carousel .owl-wrapper-outer.autoHeight{
+	-webkit-transition: height 500ms ease-in-out;
+	-moz-transition: height 500ms ease-in-out;
+	-ms-transition: height 500ms ease-in-out;
+	-o-transition: height 500ms ease-in-out;
+	transition: height 500ms ease-in-out;
+}
+	
+.owl-carousel .owl-item{
+	float: left;
+}
+.owl-controls .owl-page,
+.owl-controls .owl-buttons div{
+	cursor: pointer;
+}
+.owl-controls {
+	-webkit-user-select: none;
+	-khtml-user-select: none;
+	-moz-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+	-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+}
+
+/* mouse grab icon */
+.grabbing { 
+    cursor:url(grabbing.png) 8 8, move;
+}
+
+/* fix */
+.owl-carousel  .owl-wrapper,
+.owl-carousel  .owl-item{
+	-webkit-backface-visibility: hidden;
+	-moz-backface-visibility:    hidden;
+	-ms-backface-visibility:     hidden;
+  -webkit-transform: translate3d(0,0,0);
+  -moz-transform: translate3d(0,0,0);
+  -ms-transform: translate3d(0,0,0);
+}
+
+/* CSS3 Transitions */
+
+.owl-origin {
+	-webkit-perspective: 1200px;
+	-webkit-perspective-origin-x : 50%;
+	-webkit-perspective-origin-y : 50%;
+	-moz-perspective : 1200px;
+	-moz-perspective-origin-x : 50%;
+	-moz-perspective-origin-y : 50%;
+	perspective : 1200px;
+}
+/* fade */
+.owl-fade-out {
+  z-index: 10;
+  -webkit-animation: fadeOut .7s both ease;
+  -moz-animation: fadeOut .7s both ease;
+  animation: fadeOut .7s both ease;
+}
+.owl-fade-in {
+  -webkit-animation: fadeIn .7s both ease;
+  -moz-animation: fadeIn .7s both ease;
+  animation: fadeIn .7s both ease;
+}
+/* backSlide */
+.owl-backSlide-out {
+  -webkit-animation: backSlideOut 1s both ease;
+  -moz-animation: backSlideOut 1s both ease;
+  animation: backSlideOut 1s both ease;
+}
+.owl-backSlide-in {
+  -webkit-animation: backSlideIn 1s both ease;
+  -moz-animation: backSlideIn 1s both ease;
+  animation: backSlideIn 1s both ease;
+}
+/* goDown */
+.owl-goDown-out {
+  -webkit-animation: scaleToFade .7s ease both;
+  -moz-animation: scaleToFade .7s ease both;
+  animation: scaleToFade .7s ease both;
+}
+.owl-goDown-in {
+  -webkit-animation: goDown .6s ease both;
+  -moz-animation: goDown .6s ease both;
+  animation: goDown .6s ease both;
+}
+/* scaleUp */
+.owl-fadeUp-in {
+  -webkit-animation: scaleUpFrom .5s ease both;
+  -moz-animation: scaleUpFrom .5s ease both;
+  animation: scaleUpFrom .5s ease both;
+}
+
+.owl-fadeUp-out {
+  -webkit-animation: scaleUpTo .5s ease both;
+  -moz-animation: scaleUpTo .5s ease both;
+  animation: scaleUpTo .5s ease both;
+}
+/* Keyframes */
+/*empty*/
+@-webkit-keyframes empty {
+  0% {opacity: 1}
+}
+@-moz-keyframes empty {
+  0% {opacity: 1}
+}
+@keyframes empty {
+  0% {opacity: 1}
+}
+@-webkit-keyframes fadeIn {
+  0% { opacity:0; }
+  100% { opacity:1; }
+}
+@-moz-keyframes fadeIn {
+  0% { opacity:0; }
+  100% { opacity:1; }
+}
+@keyframes fadeIn {
+  0% { opacity:0; }
+  100% { opacity:1; }
+}
+@-webkit-keyframes fadeOut {
+  0% { opacity:1; }
+  100% { opacity:0; }
+}
+@-moz-keyframes fadeOut {
+  0% { opacity:1; }
+  100% { opacity:0; }
+}
+@keyframes fadeOut {
+  0% { opacity:1; }
+  100% { opacity:0; }
+}
+@-webkit-keyframes backSlideOut {
+  25% { opacity: .5; -webkit-transform: translateZ(-500px); }
+  75% { opacity: .5; -webkit-transform: translateZ(-500px) translateX(-200%); }
+  100% { opacity: .5; -webkit-transform: translateZ(-500px) translateX(-200%); }
+}
+@-moz-keyframes backSlideOut {
+  25% { opacity: .5; -moz-transform: translateZ(-500px); }
+  75% { opacity: .5; -moz-transform: translateZ(-500px) translateX(-200%); }
+  100% { opacity: .5; -moz-transform: translateZ(-500px) translateX(-200%); }
+}
+@keyframes backSlideOut {
+  25% { opacity: .5; transform: translateZ(-500px); }
+  75% { opacity: .5; transform: translateZ(-500px) translateX(-200%); }
+  100% { opacity: .5; transform: translateZ(-500px) translateX(-200%); }
+}
+@-webkit-keyframes backSlideIn {
+  0%, 25% { opacity: .5; -webkit-transform: translateZ(-500px) translateX(200%); }
+  75% { opacity: .5; -webkit-transform: translateZ(-500px); }
+  100% { opacity: 1; -webkit-transform: translateZ(0) translateX(0); }
+}
+@-moz-keyframes backSlideIn {
+  0%, 25% { opacity: .5; -moz-transform: translateZ(-500px) translateX(200%); }
+  75% { opacity: .5; -moz-transform: translateZ(-500px); }
+  100% { opacity: 1; -moz-transform: translateZ(0) translateX(0); }
+}
+@keyframes backSlideIn {
+  0%, 25% { opacity: .5; transform: translateZ(-500px) translateX(200%); }
+  75% { opacity: .5; transform: translateZ(-500px); }
+  100% { opacity: 1; transform: translateZ(0) translateX(0); }
+}
+@-webkit-keyframes scaleToFade {
+  to { opacity: 0; -webkit-transform: scale(.8); }
+}
+@-moz-keyframes scaleToFade {
+  to { opacity: 0; -moz-transform: scale(.8); }
+}
+@keyframes scaleToFade {
+  to { opacity: 0; transform: scale(.8); }
+}
+@-webkit-keyframes goDown {
+  from { -webkit-transform: translateY(-100%); }
+}
+@-moz-keyframes goDown {
+  from { -moz-transform: translateY(-100%); }
+}
+@keyframes goDown {
+  from { transform: translateY(-100%); }
+}
+
+@-webkit-keyframes scaleUpFrom {
+  from { opacity: 0; -webkit-transform: scale(1.5); }
+}
+@-moz-keyframes scaleUpFrom {
+  from { opacity: 0; -moz-transform: scale(1.5); }
+}
+@keyframes scaleUpFrom {
+  from { opacity: 0; transform: scale(1.5); }
+}
+
+@-webkit-keyframes scaleUpTo {
+  to { opacity: 0; -webkit-transform: scale(1.5); }
+}
+@-moz-keyframes scaleUpTo {
+  to { opacity: 0; -moz-transform: scale(1.5); }
+}
+@keyframes scaleUpTo {
+  to { opacity: 0; transform: scale(1.5); }
+}

+ 221 - 0
frontend/saas-portal-web/src/css/slit-slider.css

@@ -0,0 +1,221 @@
+.sl-slide, .sl-slides-wrapper, .sl-slide-inner {
+	position: absolute;
+	width: 100%;
+	height: 100%;
+	top: 0;
+	left: 0;
+}
+
+.sl-slider-wrapper {
+	width: 100%;
+	height: 600px;
+	overflow: hidden;
+	position: relative;
+}
+
+
+
+.bg-img {
+	background-position: center center;
+	background-repeat: no-repeat;
+	background-size: cover;
+	height: 100%;
+	width: 100%;
+}
+
+/* Custom navigation arrows */
+
+.nav-arrows span {
+	position: absolute;
+	z-index: 2000;
+	top: 50%;
+	width: 40px;
+	height: 40px;
+	border: 8px solid #ddd;
+	border: 8px solid rgba(150,150,150,0.4);
+	text-indent: -90000px;
+	margin-top: -40px;
+	cursor: pointer;
+	
+	-webkit-transform: rotate(45deg);
+	-moz-transform: rotate(45deg);
+	-o-transform: rotate(45deg);
+	-ms-transform: rotate(45deg);
+	transform: rotate(45deg);
+}
+
+.nav-arrows span:hover {
+	border-color: rgba(150,150,150,0.9);
+}
+
+.nav-arrows span.nav-arrow-prev {
+	left: 5%;
+	border-right: none;
+	border-top: none;
+}
+
+.nav-arrows span.nav-arrow-next {
+	right: 5%;
+	border-left: none;
+	border-bottom: none;
+}
+
+
+/* Custom navigation dots */
+
+.nav-dots {
+	text-align: center;
+	position: absolute;
+	bottom: 2%;
+	height: 30px;
+	width: 100%;
+	left: 0;
+	z-index: 1000;
+}
+
+.nav-dots span {
+	display: inline-block;
+	position: relative;
+	width: 16px;
+	height: 16px;
+	border-radius: 50%;
+	margin: 3px;
+	background: #ddd;
+	background: rgba(150,150,150,0.4);
+	cursor: pointer;
+	box-shadow: 
+		0 1px 1px rgba(255,255,255,0.4), 
+		inset 0 1px 1px rgba(0,0,0,0.1);
+}
+
+.nav-dots span {
+	background: rgba(150,150,150,0.1);
+	margin: 6px;
+	-webkit-transition: all 0.2s;
+	-moz-transition: all 0.2s;
+	-ms-transition: all 0.2s;
+	-o-transition: all 0.2s;
+	transition: all 0.2s;
+	box-shadow: 
+		0 1px 1px rgba(255,255,255,0.4), 
+		inset 0 1px 1px rgba(0,0,0,0.1),
+		0 0 0 2px rgba(255,255,255,0.5);
+}
+
+.nav-dots span.nav-dot-current,
+.nav-dots span:hover {
+	box-shadow: 
+		0 1px 1px rgba(255,255,255,0.4), 
+		inset 0 1px 1px rgba(0,0,0,0.1),
+		0 0 0 5px rgba(255,255,255,0.5);
+}
+
+.nav-dots span.nav-dot-current:after {
+	content: "";
+	position: absolute;
+	width: 10px;
+	height: 10px;
+	top: 3px;
+	left: 3px;
+	border-radius: 50%;
+	background: rgba(255,255,255,0.8);
+}
+
+
+
+/* Animations for content elements */
+
+.sl-trans-elems .caption-content h2 {
+	-webkit-animation: moveUp 1s ease-in-out both;
+	-moz-animation: moveUp 1s ease-in-out both;
+	-o-animation: moveUp 1s ease-in-out both;
+	-ms-animation: moveUp 1s ease-in-out both;
+	animation: moveUp 1s ease-in-out both;
+}
+.sl-trans-elems .caption-content span,
+.sl-trans-elems .caption-content a {
+	-webkit-animation: fadeIn 0.5s linear 0.5s both;
+	-moz-animation: fadeIn 0.5s linear 0.5s both;
+	-o-animation: fadeIn 0.5s linear 0.5s both;
+	-ms-animation: fadeIn 0.5s linear 0.5s both;
+	animation: fadeIn 0.5s linear 0.5s both;
+}
+
+.sl-trans-back-elems .caption-content h2 {
+	-webkit-animation: fadeOut 1s ease-in-out both;
+	-moz-animation: fadeOut 1s ease-in-out both;
+	-o-animation: fadeOut 1s ease-in-out both;
+	-ms-animation: fadeOut 1s ease-in-out both;
+	animation: fadeOut 1s ease-in-out both;
+}
+.sl-trans-back-elems .caption-content span,,
+.sl-trans-back-elems .caption-content a {
+	-webkit-animation: fadeOut 1s linear both;
+	-moz-animation: fadeOut 1s linear both;
+	-o-animation: fadeOut 1s linear both;
+	-ms-animation: fadeOut 1s linear both;
+	animation: fadeOut 1s linear both;
+}
+
+@-webkit-keyframes moveUp{
+	0% {-webkit-transform: translateY(40px);}
+	100% {-webkit-transform: translateY(0px);}
+}
+@-moz-keyframes moveUp{
+	0% {-moz-transform: translateY(40px);}
+	100% {-moz-transform: translateY(0px);}
+}
+@-o-keyframes moveUp{
+	0% {-o-transform: translateY(40px);}
+	100% {-o-transform: translateY(0px);}
+}
+@-ms-keyframes moveUp{
+	0% {-ms-transform: translateY(40px);}
+	100% {-ms-transform: translateY(0px);}
+}
+@keyframes moveUp{
+	0% {transform: translateY(40px);}
+	100% {transform: translateY(0px);}
+}
+
+@-webkit-keyframes fadeIn{
+	0% {opacity: 0;}
+	100% {opacity: 1;}
+}
+@-moz-keyframes fadeIn{
+	0% {opacity: 0;}
+	100% {opacity: 1;}
+}
+@-o-keyframes fadeIn{
+	0% {opacity: 0;}
+	100% {opacity: 1;}
+}
+@-ms-keyframes fadeIn{
+	0% {opacity: 0;}
+	100% {opacity: 1;}
+}
+@keyframes fadeIn{
+	0% {opacity: 0;}
+	100% {opacity: 1;}
+}
+
+@-webkit-keyframes fadeOut{
+	0% {opacity: 1;}
+	100% {opacity: 0;}
+}
+@-moz-keyframes fadeOut{
+	0% {opacity: 1;}
+	100% {opacity: 0;}
+}
+@-o-keyframes fadeOut{
+	0% {opacity: 1;}
+	100% {opacity: 0;}
+}
+@-ms-keyframes fadeOut{
+	0% {opacity: 1;}
+	100% {opacity: 0;}
+}
+@keyframes fadeOut{
+	0% {opacity: 1;}
+	100% {opacity: 0;}
+}

BIN
frontend/saas-portal-web/src/fonts/ionicons.ttf


BIN
frontend/saas-portal-web/src/fonts/ionicons.woff


BIN
frontend/saas-portal-web/src/img/features.jpg


BIN
frontend/saas-portal-web/src/img/icons/quotes.png


BIN
frontend/saas-portal-web/src/img/logo.png


BIN
frontend/saas-portal-web/src/img/member-1.jpg


BIN
frontend/saas-portal-web/src/img/parallax/testimonial.jpg


BIN
frontend/saas-portal-web/src/img/portfolio/item-1.jpg


BIN
frontend/saas-portal-web/src/img/portfolio/item-2.jpg


Некоторые файлы не были показаны из-за большого количества измененных файлов