Antonio Dobre 1 year ago
commit
80da2237b5
100 changed files with 16151 additions and 0 deletions
  1. 1 0
      .gitignore
  2. 218 0
      Controller/FormController.php
  3. 58 0
      Controller/LogController.php
  4. 130 0
      Controller/OrderOrProformaController.php
  5. 37 0
      Controller/ProductController.php
  6. 51 0
      Controller/StockController.php
  7. 91 0
      Controller/SyncController.php
  8. 265 0
      Core/App/Entity/Order.php
  9. 169 0
      Core/App/Entity/OrderItem.php
  10. 151 0
      Core/App/Entity/Product.php
  11. 364 0
      Core/App/Entity/Proforma.php
  12. 8 0
      Core/App/Entity/ProformaItem.php
  13. 245 0
      Core/App/Entity/Settings.php
  14. 40 0
      Core/App/Entity/Stock.php
  15. 54 0
      Core/App/Factory/EntityFactory.php
  16. 162 0
      Core/App/Factory/OrderFactory.php
  17. 249 0
      Core/App/Factory/OrderItemFactory.php
  18. 39 0
      Core/App/Factory/ProductFactory.php
  19. 78 0
      Core/App/Factory/ProformaFactory.php
  20. 44 0
      Core/App/Factory/ProformaItemFactory.php
  21. 100 0
      Core/App/Factory/SettingsFactory.php
  22. 23 0
      Core/App/Factory/StockFactory.php
  23. 9 0
      Core/App/Repository/Facturis/CustomerInterface.php
  24. 8 0
      Core/App/Repository/Facturis/OrderInterface.php
  25. 13 0
      Core/App/Repository/Facturis/ProductInterface.php
  26. 8 0
      Core/App/Repository/Facturis/ProformaInterface.php
  27. 22 0
      Core/App/Repository/Facturis/StockInterface.php
  28. 31 0
      Core/App/Repository/Marketplace/OrderInterface.php
  29. 9 0
      Core/App/Repository/Marketplace/ProductInterface.php
  30. 8 0
      Core/App/Repository/Marketplace/ProformaInterface.php
  31. 36 0
      Core/App/Repository/Marketplace/SettingsInterface.php
  32. 10 0
      Core/App/Repository/Marketplace/StockInterface.php
  33. 52 0
      Core/App/Service/OrderService.php
  34. 31 0
      Core/App/Service/ProductService.php
  35. 55 0
      Core/App/Service/ProformaService.php
  36. 89 0
      Core/App/Service/SettingsService.php
  37. 35 0
      Core/App/Service/StockService.php
  38. 16 0
      Core/FacturisRepository/CustomerRepository.php
  39. 14 0
      Core/FacturisRepository/OrderRepository.php
  40. 56 0
      Core/FacturisRepository/ProductRepository.php
  41. 14 0
      Core/FacturisRepository/ProformaRepository.php
  42. 241 0
      Core/FacturisRepository/Repository.php
  43. 125 0
      Core/FacturisRepository/StockRepository.php
  44. 20 0
      Core/Language/AnyLanguage.php
  45. 122 0
      Core/Language/English.php
  46. 125 0
      Core/Language/Romanian.php
  47. 0 0
      Core/Language/index.php
  48. 8 0
      Core/Log/AnyLog.php
  49. 35 0
      Core/Log/FileLog.php
  50. 273 0
      Database.php
  51. 229 0
      FacturisSync.php
  52. 297 0
      MarketplaceRepository/OrderRepository.php
  53. 68 0
      MarketplaceRepository/ProductRepository.php
  54. 118 0
      MarketplaceRepository/ProformaRepository.php
  55. 246 0
      MarketplaceRepository/Repository.php
  56. 258 0
      MarketplaceRepository/SettingsRepository.php
  57. 101 0
      MarketplaceRepository/StockRepository.php
  58. 365 0
      View/css/form.css
  59. 305 0
      View/css/main.css
  60. BIN
      View/img/loader.gif
  61. 0 0
      View/index.php
  62. 40 0
      View/js/tab1.js
  63. 103 0
      View/js/tab2.js
  64. 71 0
      View/js/tab3.js
  65. 30 0
      View/js/tab4.js
  66. 54 0
      View/js/tab5.js
  67. 144 0
      View/main.php
  68. 27 0
      View/tab1.php
  69. 53 0
      View/tab2.php
  70. 98 0
      View/tab3.php
  71. 76 0
      View/tab4.php
  72. 38 0
      View/tab5.php
  73. 72 0
      View/vanillajs-datepicker/CHANGELOG.md
  74. 21 0
      View/vanillajs-datepicker/LICENSE
  75. 31 0
      View/vanillajs-datepicker/README.md
  76. 538 0
      View/vanillajs-datepicker/demo/bs4.html
  77. 599 0
      View/vanillajs-datepicker/demo/foundation.html
  78. 547 0
      View/vanillajs-datepicker/demo/index.html
  79. 469 0
      View/vanillajs-datepicker/demo/live-demo.js
  80. 584 0
      View/vanillajs-datepicker/demo/plain-css.html
  81. 276 0
      View/vanillajs-datepicker/dist/css/datepicker-bs4.css
  82. 1 0
      View/vanillajs-datepicker/dist/css/datepicker-bs4.min.css
  83. 258 0
      View/vanillajs-datepicker/dist/css/datepicker-bulma.css
  84. 1 0
      View/vanillajs-datepicker/dist/css/datepicker-bulma.min.css
  85. 262 0
      View/vanillajs-datepicker/dist/css/datepicker-foundation.css
  86. 1 0
      View/vanillajs-datepicker/dist/css/datepicker-foundation.min.css
  87. 306 0
      View/vanillajs-datepicker/dist/css/datepicker.css
  88. 1 0
      View/vanillajs-datepicker/dist/css/datepicker.min.css
  89. 2757 0
      View/vanillajs-datepicker/dist/js/datepicker-full.js
  90. 1 0
      View/vanillajs-datepicker/dist/js/datepicker-full.min.js
  91. 2549 0
      View/vanillajs-datepicker/dist/js/datepicker.js
  92. 1 0
      View/vanillajs-datepicker/dist/js/datepicker.min.js
  93. 15 0
      View/vanillajs-datepicker/dist/js/locales/ar-tn.js
  94. 15 0
      View/vanillajs-datepicker/dist/js/locales/ar.js
  95. 14 0
      View/vanillajs-datepicker/dist/js/locales/az.js
  96. 14 0
      View/vanillajs-datepicker/dist/js/locales/bg.js
  97. 18 0
      View/vanillajs-datepicker/dist/js/locales/bm.js
  98. 19 0
      View/vanillajs-datepicker/dist/js/locales/bn.js
  99. 18 0
      View/vanillajs-datepicker/dist/js/locales/br.js
  100. 0 0
      View/vanillajs-datepicker/dist/js/locales/bs.js

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+.idea/

+ 218 - 0
Controller/FormController.php

@@ -0,0 +1,218 @@
+<?php 
+
+namespace Controller;
+
+class FormController
+{
+
+    public function getForm($db, $language)
+    {
+		$data = array();
+        $settingsRepository = new \MarketplaceRepository\SettingsRepository($db);	
+	
+		$settingsService = new \Core\App\Service\SettingsService(
+			$settingsRepository
+        );
+        
+        $data['title'] = $language->get('title');
+        $data['description'] = $language->get('description');
+
+        //tab1 
+        $data['tab_name_1'] = $language->get('tab_name_1'); 
+        $data['tab_description_1'] = $language->get('tab_description_1'); 
+
+        $dataTypes = $settingsService->getSyncDataTypes(); 
+		$data['dataTypes'] = array(array('id' => '', 'label' =>  $language->get('data_type')));
+		if(!empty($dataTypes))
+		{
+			foreach($dataTypes as $dataType)
+			{
+				$data['dataTypes'][] = array('id' => $dataType, 'label' => $language->get($dataType));
+			}
+        }
+
+        sort($data['dataTypes']);
+        $data['sync_btn'] = $language->get('sync_btn');
+        $data['download_btn'] = $language->get('download_btn');
+        $data['clear_btn'] = $language->get('clear_btn');
+        $data['datatype_error'] = $language->get('datatype_error');
+        $data['authform_error'] = $language->get('authform_error');
+        $data['sync_please_wait'] = $language->get('sync_please_wait');
+        $data['download_log_error_1'] = $language->get('download_log_error_1');
+        $data['download_log_error_2'] = $language->get('download_log_error_2');
+        
+        $data['select_text'] = $language->get('select_text');
+        
+
+        //tab 2
+        $data['tab_name_2'] = $language->get('tab_name_2'); 
+        $data['tab_description_2'] = $language->get('tab_description_2'); 
+
+        $data['auth_apikey'] = $language->get('auth_api_key'); 
+        $data['auth_username'] = $language->get('auth_username'); 
+        $data['auth_password'] = $language->get('auth_password'); 
+        $data['auth_fiscalcode'] = $language->get('auth_fiscal_code'); 
+
+        $data['auth_apikey_error'] = $language->get('auth_apikey_error'); 
+        $data['auth_username_error'] = $language->get('auth_username_error'); 
+        $data['auth_password_error'] = $language->get('auth_password_error'); 
+        $data['auth_fiscalcode_error'] = $language->get('auth_fiscalcode_error'); 
+        $data['save_btn'] = $language->get('save_btn');
+
+        //tab 3
+        $data['tab_name_3'] = $language->get('tab_name_3'); 
+        $data['tab_name_3_1'] = $language->get('tab_name_3_1'); 
+        $data['tab_name_3_2'] = $language->get('tab_name_3_2'); 
+
+		$data['with_discount'] = $language->get('with_discount'); 
+        $data['with_discount_desc'] = $language->get('with_discount_desc'); 
+        //tab4 
+        $data['tab_name_4'] = $language->get('tab_name_4'); 
+
+		//tab 5
+        $data['tab_name_5'] = $language->get('tab_name_5'); 
+        $data['datepicker'] = $language->get('datepicker');
+        $data['tab_description_5_1'] = $language->get('tab_description_5_1'); 
+        $data['tab_description_5_2'] = $language->get('tab_description_5_2'); 
+        $data['confirm_clear_log'] = $language->get('confirm_clear_log');
+
+
+        $syncOrdersAs =  $settingsService->getSyncOrdersAs(); 
+		$data['syncOrdersAs'] = array();
+		if(!empty($syncOrdersAs))
+		{
+			foreach($syncOrdersAs as $syncOrdersAsElement)
+			{
+				$data['syncOrdersAs'][] = array('id' => $syncOrdersAsElement, 'label' => $language->get($syncOrdersAsElement));
+			}
+        }
+        $data['sync_orders_as'] = $language->get('sync_orders_as');
+        $data['proforma_series'] = $language->get('proforma_series');
+        $data['days_ago'] = $language->get('days_ago');
+        $data['auto_order_sync'] = $language->get('auto_order_sync');
+        $data['auto_stock_sync'] = $language->get('auto_stock_sync');
+
+        $autoSyncOptions =  $settingsService->getAutoSyncOptions(); 
+        $data['autoSyncOptions'] = array();
+		if(!empty($autoSyncOptions))
+		{
+			foreach($autoSyncOptions as $autoSyncOptionsElement)
+			{
+				$data['autoSyncOptions'][] = array('id' => $autoSyncOptionsElement, 'label' => $language->get($autoSyncOptionsElement));
+			}
+        }
+
+		$data['shopLocations'] = $this->getShopLocations($settingsRepository);
+        $data['locations'] = $language->get('locations');
+		
+		$enabledDisabledOptions =  $settingsService->getEnabledDisabledOptions(); 
+		$data['withDiscountOptions'] = array();
+		if(!empty($enabledDisabledOptions))
+		{
+			foreach($enabledDisabledOptions as $enabledDisabledOption)
+			{
+				$data['withDiscountOptions'][] = array('id' => $enabledDisabledOption, 'label' => $language->get($enabledDisabledOption));
+			}
+        }
+        $data['filter_stock'] = $language->get('filter_stock');
+        $data['loading_data_please_wait'] = $language->get('loading_data_please_wait');
+
+        $data['option_proformaserie_error'] = $language->get('option_proformaserie_error');
+        $data['option_daysago_error'] = $language->get('option_daysago_error');
+        $data['version_text'] = $language->get('version_text');
+        
+        $data['option'] = $settingsService->getOptions();  
+        
+        $data['auth'] = $settingsService->getAuth(); 
+
+        $data['version_number'] = $settingsService->getVersion();
+        $data['version_link'] = $settingsService->getVersionLink();
+        $data['check_latest_version'] = $language->get('check_latest_version');
+        
+        //tutorials
+        $data['tutorial_title'] = $language->get('tutorial_title');
+        $data['tutorial_p'] = $language->get('tutorial_p');
+        $data['tutorial_1'] = $language->get('tutorial_1');
+        $data['tutorial_2'] = $language->get('tutorial_2');
+        $data['tutorial_3'] = $language->get('tutorial_3');
+        $data['tutorial_4'] = $language->get('tutorial_4');
+        $data['show_tutorial'] = $settingsService->showTutorial(); 
+
+        $data['authform_error'] = $language->get('authform_error');
+        
+       
+        return $data;
+        
+    }
+
+    public function testAuth($db)
+    {
+        $this->settingsRepository = new \MarketplaceRepository\SettingsRepository($db);  
+        $settingsService = new \Core\App\Service\SettingsService(
+            $this->settingsRepository
+        );
+
+        $remoteStockRepository = new \Core\FacturisRepository\StockRepository(
+            $_GET['fsync_auth_apikey'],
+            $_GET['fsync_auth_username'],
+            $_GET['fsync_auth_password'],
+            $_GET['fsync_auth_fiscalcode']
+        );
+
+        $settingsService->testAuth($remoteStockRepository);  
+    }
+
+    public function getPdlGestiuni($db, $language)
+    {
+        $settingsRepository = new \MarketplaceRepository\SettingsRepository($db);  
+        $settingsService = new \Core\App\Service\SettingsService(
+            $settingsRepository
+        );
+                
+        $remoteStockRepository = new \Core\FacturisRepository\StockRepository(
+            $settingsRepository->getAuthApiKey(),
+            $settingsRepository->getAuthUsername(),
+            $settingsRepository->getAuthPassword(),
+            $settingsRepository->getAuthFiscalCode()
+        );	
+            
+        $pdl_gestiuni = $settingsService->getPdlGestiuni($remoteStockRepository); 
+        
+        $result = '';
+        $result .= '<option value=""';
+        $result .= (isset($_GET['fsync_option_filterstock']) && $_GET['fsync_option_filterstock'] == '') ? ' selected="selected" ' : '';
+        $result .= ' >' . $language->get('filter_stock_all') . '</option>';
+
+        if(!empty($pdl_gestiuni))
+        {
+            foreach($pdl_gestiuni as $pdl_gestiune)
+            {
+                $result .= '<option value=' . $pdl_gestiune['id'] . ' ';
+                $result .= (isset($_GET['fsync_option_filterstock']) && $_GET['fsync_option_filterstock'] == $pdl_gestiune['id']) ? ' selected="selected" ' : '';
+                $result .= ' >' . $pdl_gestiune['name'] . '</option>';
+            }
+        }
+
+        echo $result;
+    }
+
+    public function checkLatestVersion($db, $language)
+    {
+        $settingsRepository = new \MarketplaceRepository\SettingsRepository($db);  
+        $settingsService = new \Core\App\Service\SettingsService(
+            $settingsRepository
+        );
+
+        $settingsService->checkLatestVersion($language);
+    }
+
+    private function getShopLocations($settingsRepository)
+    {
+        $marketplaceStockRepository = new \MarketplaceRepository\StockRepository(
+            $settingsRepository->getToken(),
+            $settingsRepository->getShop()
+        );
+    
+        return $marketplaceStockRepository->getShopLocations(); 
+    }
+}

+ 58 - 0
Controller/LogController.php

@@ -0,0 +1,58 @@
+<?php 
+
+namespace Controller;
+
+class LogController
+{
+    const FIELDS = 4;
+
+    public function downloadFileLog($startDate, $stopDate, $header)
+    {
+        $file = \Core\Log\FileLog::getFile();
+
+        header("Content-Type: text/csv");
+        header("Content-Disposition: attachment; filename=" . basename(str_replace('.log', '.csv', $file)));
+        header("Cache-Control: no-cache, no-store, must-revalidate");
+        header("Pragma: no-cache");
+        header("Expires: 0");
+    
+        $output = fopen("php://output", "w");
+
+        if (file_exists($file)) {
+
+            $f = fopen($file, "r");
+            fputcsv($output, $header);
+            while(!feof($f)) {
+                $row = explode(\Core\Log\FileLog::DATA_SEPARATOR, fgets($f));
+                $rowDate = date('Y-m-d', strtotime($row[0]));
+                if(
+                    count($row) == self::FIELDS && 
+                    strtotime($rowDate) >= strtotime($startDate) && 
+                    strtotime($rowDate) <= strtotime($stopDate)
+                )
+                {
+                    $row[0] = date('d.m.Y H:i:s', strtotime($row[0]));
+                    fputcsv($output, array_slice($row, 0, self::FIELDS - 1));
+                }
+            }
+            fclose($output);
+            fclose($f);
+            exit;
+		} else {
+			throw new \Exception('Error');
+		}
+    }
+
+    public function clearFileLog()
+    {
+        $file = \Core\Log\FileLog::getFile();
+        if (file_exists($file) && filesize($file) > 0) {
+
+            $f = fopen($file, "w+");
+            fclose($f);
+
+		} else {
+			throw new \Exception('Error');
+		}
+    }
+}

+ 130 - 0
Controller/OrderOrProformaController.php

@@ -0,0 +1,130 @@
+<?php 
+
+namespace Controller;
+
+class OrderOrProformaController
+{
+    public $marketplaceOrders;
+
+    public $settingsRepository;
+
+    public function __construct($settingsRepository)
+    {
+        $this->settingsRepository = $settingsRepository;
+    }
+
+    public function sync()
+    {
+        if($this->settingsRepository->getOptionOrderOrProforma() == \Core\App\Factory\SettingsFactory::SELECT_ORDER)
+        {
+            $this->syncOrders();
+            return \Core\App\Factory\SettingsFactory::SELECT_ORDER;
+        }
+
+        if($this->settingsRepository->getOptionOrderOrProforma() == \Core\App\Factory\SettingsFactory::SELECT_PROFORMA)
+        {
+            $this->syncProformas();
+            return \Core\App\Factory\SettingsFactory::SELECT_PROFORMA;
+        }
+    }
+
+    public function syncOrders()
+    {
+        $facturisCustomerRepository = new \Core\FacturisRepository\CustomerRepository(
+            $this->settingsRepository->getAuthApiKey(),
+            $this->settingsRepository->getAuthUsername(),
+            $this->settingsRepository->getAuthPassword(),
+            $this->settingsRepository->getAuthFiscalCode()
+        );
+		
+		 $facturisProductRepository = new \Core\FacturisRepository\ProductRepository(
+            $this->settingsRepository->getAuthApiKey(),
+            $this->settingsRepository->getAuthUsername(),
+            $this->settingsRepository->getAuthPassword(),
+            $this->settingsRepository->getAuthFiscalCode()
+        );
+        
+        $marketplaceOrderRepository = new \MarketplaceRepository\OrderRepository(
+            $this->settingsRepository->getToken(),
+            $this->settingsRepository->getShop()
+        );
+
+        $marketplaceProductRepository = new \MarketplaceRepository\ProductRepository(
+            $this->settingsRepository->getToken(),
+            $this->settingsRepository->getShop()
+        );
+
+        $facturisOrderRepository = new \Core\FacturisRepository\OrderRepository(
+            $this->settingsRepository->getAuthApiKey(),
+            $this->settingsRepository->getAuthUsername(),
+            $this->settingsRepository->getAuthPassword(),
+            $this->settingsRepository->getAuthFiscalCode()
+        );
+    
+        $service = new \Core\App\Service\OrderService(
+            $marketplaceOrderRepository,
+            $marketplaceProductRepository,
+            $facturisCustomerRepository,
+            $facturisProductRepository,
+            $facturisOrderRepository,
+            $this->settingsRepository
+        );
+        
+        $service->sync();
+    }
+
+    public function syncProformas()
+    {
+        $facturisCustomerRepository = new \Core\FacturisRepository\CustomerRepository(
+            $this->settingsRepository->getAuthApiKey(),
+            $this->settingsRepository->getAuthUsername(),
+            $this->settingsRepository->getAuthPassword(),
+            $this->settingsRepository->getAuthFiscalCode()
+        );
+
+		$facturisProductRepository = new \Core\FacturisRepository\ProductRepository(
+            $this->settingsRepository->getAuthApiKey(),
+            $this->settingsRepository->getAuthUsername(),
+            $this->settingsRepository->getAuthPassword(),
+            $this->settingsRepository->getAuthFiscalCode()
+        );
+        
+        $marketplaceProformaRepository = new \MarketplaceRepository\ProformaRepository(
+            $this->settingsRepository->getToken(),
+            $this->settingsRepository->getShop()
+        );
+
+        $marketplaceProductRepository = new \MarketplaceRepository\ProductRepository(
+            $this->settingsRepository->getToken(),
+            $this->settingsRepository->getShop()
+        );
+
+        $facturisProformaRepository = new \Core\FacturisRepository\ProformaRepository(
+            $this->settingsRepository->getAuthApiKey(),
+            $this->settingsRepository->getAuthUsername(),
+            $this->settingsRepository->getAuthPassword(),
+            $this->settingsRepository->getAuthFiscalCode()
+        );
+    
+        $service = new \Core\App\Service\ProformaService(
+            $marketplaceProformaRepository,
+            $marketplaceProductRepository,
+            $facturisCustomerRepository,
+            $facturisProductRepository,
+            $facturisProformaRepository,
+            $this->settingsRepository
+        );
+        
+        $service->sync();
+    }
+
+    public function autoSync()
+    {
+        if($this->settingsRepository->getOptionAutoOrderSync() == \Core\App\Factory\SettingsFactory::AUTO_SYNC_ENABLED)
+        {
+            $this->sync();
+        }
+    }
+
+
+}

+ 37 - 0
Controller/ProductController.php

@@ -0,0 +1,37 @@
+<?php 
+
+namespace Controller;
+
+class ProductController
+{
+    public $settingsRepository;
+
+    public function __construct($settingsRepository)
+    {
+        $this->settingsRepository = $settingsRepository;
+    }
+
+    public function sync()
+    {
+        $marketplaceProductRepository = new \MarketplaceRepository\ProductRepository(
+            $this->settingsRepository->getToken(),
+            $this->settingsRepository->getShop()
+        );    
+
+        $facturisProductRepository = new \Core\FacturisRepository\ProductRepository(
+            $this->settingsRepository->getAuthApiKey(),
+            $this->settingsRepository->getAuthUsername(),
+            $this->settingsRepository->getAuthPassword(),
+            $this->settingsRepository->getAuthFiscalCode()
+        );
+
+        $service = new \Core\App\Service\ProductService(
+            $marketplaceProductRepository,
+            $facturisProductRepository
+        );
+
+        $service->sync();
+
+    }
+    
+}

+ 51 - 0
Controller/StockController.php

@@ -0,0 +1,51 @@
+<?php 
+
+namespace Controller;
+
+class StockController
+{
+    public $settingsRepository;
+
+    public function __construct($settingsRepository)
+    {
+        $this->settingsRepository = $settingsRepository;
+    }
+
+    public function sync()
+    {  
+        $marketplaceStockRepository = new \MarketplaceRepository\StockRepository(
+            $this->settingsRepository->getToken(),
+            $this->settingsRepository->getShop()
+        );
+
+        $marketplaceProductRepository = new \MarketplaceRepository\ProductRepository(
+            $this->settingsRepository->getToken(),
+            $this->settingsRepository->getShop()
+        );
+
+        $facturisStockRepository = new \Core\FacturisRepository\StockRepository(
+            $this->settingsRepository->getAuthApiKey(),
+            $this->settingsRepository->getAuthUsername(),
+            $this->settingsRepository->getAuthPassword(),
+            $this->settingsRepository->getAuthFiscalCode()
+        );
+    
+        $service = new \Core\App\Service\StockService(
+            $marketplaceStockRepository,
+            $marketplaceProductRepository,
+            $facturisStockRepository,
+            $this->settingsRepository
+        );
+    
+        $service->sync();
+    }
+
+    public function autoSync()
+    {
+        if($this->settingsRepository->getOptionAutoStockSync() == \Core\App\Factory\SettingsFactory::AUTO_SYNC_ENABLED)
+        {
+            $this->sync();
+        }
+    }
+    
+}

+ 91 - 0
Controller/SyncController.php

@@ -0,0 +1,91 @@
+<?php 
+
+namespace Controller;
+
+class SyncController
+{
+    public $language;
+    const TIME_LIMIT = 18000;
+
+    public function __construct($db, $language, $shop = '')
+    {
+        set_time_limit( self::TIME_LIMIT );
+
+        $this->language = $language; 
+        $this->settingsRepository = new \MarketplaceRepository\SettingsRepository($db, $shop);  
+    }
+
+    public function sync()
+    {   
+        if(isset($_POST['fsync_datatype']))
+        {
+            switch($_POST['fsync_datatype'])
+            {
+                case 'product': $result = $this->syncProduct();break;
+                case 'order': $result = $this->syncOrderOrProforma();break;
+                case 'stock': $result = $this->syncStock();break;  
+                default: $result = '';break; 
+            }
+        }
+        else
+        {
+            $result = '';
+        }
+
+        return $result;
+    }
+
+    public function syncProduct()
+    {
+        $controller = new \Controller\ProductController(
+            $this->settingsRepository
+        );
+        $controller->sync();
+
+        return $this->language->get('success_sync_prod');
+    }
+
+    public function syncOrderOrProforma()
+    {
+        $controller = new \Controller\OrderOrProformaController(
+            $this->settingsRepository
+        );
+        $response = $controller->sync();
+
+        if($response == \Core\App\Factory\SettingsFactory::SELECT_ORDER)
+        {
+            return $this->language->get('success_sync_order');
+        }
+
+        if($response == \Core\App\Factory\SettingsFactory::SELECT_PROFORMA)
+        {
+            return $this->language->get('success_sync_proforma');
+        }
+        
+    }
+
+    public function syncStock()
+    {
+        $controller = new \Controller\StockController(
+            $this->settingsRepository
+        );
+        $controller->sync();
+
+        return $this->language->get('success_sync_stock');
+    }
+
+    public function autoSync()
+    {
+        $orderController = new \Controller\OrderOrProformaController(
+            $this->settingsRepository
+        );
+        $stockController = new \Controller\StockController(
+            $this->settingsRepository
+        );
+
+        $orderController->autoSync();
+        $stockController->autoSync();
+    }
+  
+    
+}

+ 265 - 0
Core/App/Entity/Order.php

@@ -0,0 +1,265 @@
+<?php
+
+namespace Core\App\Entity;
+
+class Order
+{
+    public $facturi_id_extern; 
+    public $facturi_numar; 
+    public $facturi_data; 
+    public $facturi_moneda; 
+
+    public $facturi_client_id; 
+    public $facturi_nume_client; 
+    public $facturi_tip_persoana;
+    public $facturi_codf_client; 
+    public $facturi_nrreg_client; 
+    public $facturi_sediu_client; 
+    public $facturi_judet_client; 
+    public $facturi_oras_client;
+    public $facturi_tel_client; 
+    public $facturi_email_client;
+    public $facturi_clienti_adresa_livrare; 
+
+    public $facturi_status; 
+    public $facturi_mod_plata; 
+    public $facturi_nume_livrare; 
+    public $facturi_oras_livrare; 
+    public $facturi_judet_livrare;
+    public $facturi_tel_livrare; 
+    public $facturi_codpostal_livrare; 
+    public $facturi_obs; 
+
+    public function getFacturiIdExtern()
+    {
+        return $this->facturi_id_extern;
+    }
+
+    public function getFacturiNumar()
+    {
+        return $this->facturi_numar;
+    }
+
+    public function getFacturiData()
+    {
+        return $this->facturi_data;
+    }
+
+    public function getFacturiMoneda()
+    {
+        return $this->facturi_moneda;
+    }
+
+    public function getFacturiClientId()
+    {
+        return $this->facturi_client_id;
+    }
+
+    public function getFacturiNumeClient()
+    {
+        return $this->facturi_nume_client;
+    }
+
+    public function getFacturiTipPersoana()
+    {
+        return $this->facturi_tip_persoana;
+    }
+
+    public function getFacturiCodfClient()
+    {
+        return $this->facturi_codf_client;
+    }
+
+    public function getFacturiNrregClient()
+    {
+        return $this->facturi_nrreg_client;
+    }
+
+    public function getFacturiSediuClient()
+    {
+        return $this->facturi_sediu_client;
+    }
+
+    public function getFacturiJudetClient()
+    {
+        return $this->facturi_judet_client;
+    }
+
+    public function getFacturiOrasClient()
+    {
+        return $this->facturi_oras_client;
+    }
+
+    public function getFacturiTelClient()
+    {
+        return $this->facturi_tel_client;
+    }
+
+    public function getFacturiEmailClient()
+    {
+        return $this->facturi_email_client;
+    }
+
+    public function getFacturiClientiAdresaLivrare()
+    {
+        return $this->facturi_clienti_adresa_livrare;
+    }
+
+
+    public function getFacturiStatus()
+    {
+        return $this->facturi_status;
+    }
+
+    public function getFacturiModPlata()
+    {
+        return $this->facturi_mod_plata;
+    }
+
+    public function getFacturiNumeLivrare()
+    {
+        return $this->facturi_nume_livrare;
+    }
+
+    public function getFacturiOrasLivrare()
+    {
+        return $this->facturi_oras_livrare;
+    }
+
+    public function getFacturiJudetLivrare()
+    {
+        return $this->facturi_judet_livrare;
+    }
+
+    public function getFacturiTelLivrare()
+    {
+        return $this->facturi_tel_livrare;
+    }
+
+    public function getFacturiCodpostalLivrare()
+    {
+        return $this->facturi_codpostal_livrare;
+    }
+
+    public function getFacturiObs()
+    {
+        return $this->facturi_obs;
+    }
+
+    public function setFacturiIdExtern($facturi_id_extern)
+    {
+        $this->facturi_id_extern = $facturi_id_extern;
+    }
+
+    public function setFacturiNumar($facturi_numar)
+    {
+        $this->facturi_numar = $facturi_numar;
+    }
+
+    public function setFacturiData($facturi_data)
+    {
+        $this->facturi_data = $facturi_data;
+    }
+
+    public function setFacturiMoneda($facturi_moneda)
+    {
+        $this->facturi_moneda = $facturi_moneda;
+    }
+
+
+    public function setFacturiClientId($facturi_client_id)
+    {
+        $this->facturi_client_id = $facturi_client_id;
+    }
+
+    public function setFacturiNumeClient($facturi_nume_client)
+    {
+        $this->facturi_nume_client = $facturi_nume_client;
+    }
+
+    public function setFacturiTipPersoana($facturi_tip_persoana)
+    {
+        $this->facturi_tip_persoana = $facturi_tip_persoana;
+    }
+
+    public function setFacturiCodfClient($facturi_codf_client)
+    {
+        $this->facturi_codf_client = $facturi_codf_client;
+    }
+
+    public function setFacturiNrregClient($facturi_nrreg_client)
+    {
+        $this->facturi_nrreg_client = $facturi_nrreg_client;
+    }
+
+    public function setFacturiSediuClient($facturi_sediu_client)
+    {
+        $this->facturi_sediu_client = $facturi_sediu_client;
+    }
+
+    public function setFacturiJudetClient($facturi_judet_client)
+    {
+        $this->facturi_judet_client = $facturi_judet_client;
+    }
+
+    public function setFacturiOrasClient($facturi_oras_client)
+    {
+        $this->facturi_oras_client = $facturi_oras_client;
+    }
+
+    public function setFacturiTelClient($facturi_tel_client)
+    {
+        $this->facturi_tel_client = $facturi_tel_client;
+    }
+
+    public function setFacturiEmailClient($facturi_email_client)
+    {
+        $this->facturi_email_client = $facturi_email_client;
+    }
+
+    public function setFacturiClientiAdresaLivrare($facturi_clienti_adresa_livrare)
+    {
+        $this->facturi_clienti_adresa_livrare = $facturi_clienti_adresa_livrare;
+    }
+
+
+    public function setFacturiStatus($facturi_status)
+    {
+        $this->facturi_status = $facturi_status;
+    }
+
+    public function setFacturiModPlata($facturi_mod_plata)
+    {
+        $this->facturi_mod_plata = $facturi_mod_plata;
+    }
+
+    public function setFacturiNumeLivrare($facturi_nume_livrare)
+    {
+        $this->facturi_nume_livrare = $facturi_nume_livrare;
+    }
+
+    public function setFacturiOrasLivrare($facturi_oras_livrare)
+    {
+        $this->facturi_oras_livrare = $facturi_oras_livrare;
+    }
+
+    public function setFacturiJudetLivrare($facturi_judet_livrare)
+    {
+        $this->facturi_judet_livrare = $facturi_judet_livrare;
+    }
+
+    public function setFacturiTelLivrare($facturi_tel_livrare)
+    {
+        $this->facturi_tel_livrare = $facturi_tel_livrare;
+    }
+
+    public function setFacturiCodpostalLivrare($facturi_codpostal_livrare)
+    {
+        $this->facturi_codpostal_livrare = $facturi_codpostal_livrare;
+    }
+
+    public function setFacturiObs($facturi_obs)
+    {
+        $this->facturi_obs = $facturi_obs;
+    }
+}

+ 169 - 0
Core/App/Entity/OrderItem.php

@@ -0,0 +1,169 @@
+<?php
+
+namespace Core\App\Entity;
+
+class OrderItem
+{
+    public $facturi_prod_nume;
+    public $facturi_prod_moneda;
+
+    public $facturi_prod_pretftva;
+    public $facturi_prod_pretctva;
+    public $facturi_prod_tva;
+    public $facturi_prod_cant;
+    public $facturi_prod_val;
+    public $facturi_prod_val_tva;
+    public $facturi_prod_val_tot;
+
+    public $facturi_prod_um;
+
+    public $prod_cod;
+    public $prod_sku;
+    public $prod_cod1;
+    public $prod_cod_cautare;
+
+    public function getFacturiProdNume()
+    {
+        return $this->facturi_prod_nume;
+    }
+
+    public function getFacturiProdMoneda()
+    {
+        return $this->facturi_prod_moneda;
+    }
+    
+    public function getFacturiProdPretftva()
+    {
+        return $this->facturi_prod_pretftva;
+    }
+        
+    public function getFacturiProdPretctva()
+    {
+        return $this->facturi_prod_pretctva;
+    }
+        
+    public function getFacturiProdTva()
+    {
+        return $this->facturi_prod_tva;
+    }
+        
+    public function getFacturiProdCant()
+    {
+        return $this->facturi_prod_cant;
+    }
+        
+    public function getFacturiProdVal()
+    {
+        return $this->facturi_prod_val;
+    }
+        
+    public function getFacturiProdValTva()
+    {
+        return $this->facturi_prod_val_tva;
+    }
+        
+    public function getFacturiProdValTot()
+    {
+        return $this->facturi_prod_val_tot;
+    }
+        
+    public function getProdCod()
+    {
+        return $this->prod_cod;
+    }
+        
+    public function getProdSku()
+    {
+        return $this->prod_sku;
+    }
+        
+    public function getProdCod1()
+    {
+        return $this->prod_cod1;
+    }
+
+    public function getFacturiProdUm()
+    {
+        return $this->facturi_prod_um;
+    }
+
+    public function getProdCodCautare()
+    {
+        return $this->prod_cod_cautare;
+    }
+
+    public function setFacturiProdNume($facturi_prod_nume)
+    {
+        $this->facturi_prod_nume = $facturi_prod_nume;
+    }
+
+    public function setFacturiProdMoneda($facturi_prod_moneda)
+    {
+        $this->facturi_prod_moneda = $facturi_prod_moneda;
+    }
+    
+
+    public function setFacturiProdPretftva($facturi_prod_pretftva)
+    {
+        $this->facturi_prod_pretftva = $facturi_prod_pretftva;
+    }
+    
+    public function setFacturiProdPretctva($facturi_prod_pretctva)
+    {
+        $this->facturi_prod_pretctva = $facturi_prod_pretctva;
+    }
+    
+    public function setFacturiProdTva($facturi_prod_tva)
+    {
+        $this->facturi_prod_tva = $facturi_prod_tva;
+    }
+    
+    public function setFacturiProdCant($facturi_prod_cant)
+    {
+        $this->facturi_prod_cant = $facturi_prod_cant;
+    }
+    
+    public function setFacturiProdVal($facturi_prod_val)
+    {
+        $this->facturi_prod_val = $facturi_prod_val;
+    }
+    
+    public function setFacturiProdValTva($facturi_prod_val_tva)
+    {
+        $this->facturi_prod_val_tva = $facturi_prod_val_tva;
+    }
+    
+    public function setFacturiProdValTot($facturi_prod_val_tot)
+    {
+        $this->facturi_prod_val_tot = $facturi_prod_val_tot;
+    }
+    
+
+    public function setFacturiProdUm($facturi_prod_um)
+    {
+        $this->facturi_prod_um = $facturi_prod_um;
+    }
+    
+
+    public function setProdCod($prod_cod)
+    {
+        $this->prod_cod = $prod_cod;
+    }
+    
+    public function setProdSku($prod_sku)
+    {
+        $this->prod_sku = $prod_sku;
+    }
+    
+    public function setProdCod1($prod_cod1)
+    {
+        $this->prod_cod1 = $prod_cod1;
+    }
+    
+    public function setProdCodCautare($prod_cod_cautare)
+    {
+        $this->prod_cod_cautare = $prod_cod_cautare;
+    }
+    
+     
+}

+ 151 - 0
Core/App/Entity/Product.php

@@ -0,0 +1,151 @@
+<?php
+
+namespace Core\App\Entity;
+
+class Product
+{
+    public $prod_nume;
+    public $prod_cod;
+    public $prod_sku;
+    public $prod_um;
+    public $prod_cod1;
+    public $fk_curs_moneda;
+    public $prod_obs1;
+    public $prod_obs2;
+    public $prod_obs3;
+    public $fk_cote_tva_nume;
+    public $prod_tip;
+    public $prod_pret_ftva;
+    public $prod_pret_ctva; 
+
+    public function setProdNume($prod_nume)
+    {
+        $this->prod_nume = $prod_nume;
+    }
+
+    public function getProdNume()
+    {
+        return $this->prod_nume;
+    }
+
+    public function setProdCod($prod_cod)
+    {
+        $this->prod_cod = $prod_cod;
+    }
+
+    public function getProdCod()
+    {
+        return $this->prod_cod;
+    }
+    public function setProdSku($prod_sku)
+    {
+        $this->prod_sku = $prod_sku;
+    }
+
+    public function getProdSku()
+    {
+        return $this->prod_sku;
+    }
+
+    public function setProdUm($prod_um)
+    {
+        $this->prod_um = $prod_um;
+    }
+
+    public function getProdUm()
+    {
+        return $this->prod_um;
+    }
+    public function setProdCod1($prod_cod1)
+    {
+        $this->prod_cod1 = $prod_cod1;
+    }
+
+    public function getProdCod1()
+    {
+        return $this->prod_cod1;
+    }
+
+    public function setFkCursMoneda($fk_curs_moneda)
+    {
+        $this->fk_curs_moneda = $fk_curs_moneda;
+    }
+
+    public function getFkCursMoneda()
+    {
+        return $this->fk_curs_moneda;
+    }
+
+    public function setProdObs1($prod_obs1)
+    {
+        $this->prod_obs1 = $prod_obs1;
+    }
+
+    public function getProdObs1()
+    {
+        return $this->prod_obs1;
+    }
+
+    public function setProdObs2($prod_obs2)
+    {
+        $this->prod_obs2 = $prod_obs2;
+    }
+
+    public function getProdObs2()
+    {
+        return $this->prod_obs2;
+    }
+
+    public function setProdObs3($prod_obs3)
+    {
+        $this->prod_obs3 = $prod_obs3;
+    }
+
+    public function getProdObs3()
+    {
+        return $this->prod_obs3;
+    }
+
+    public function setFkCoteTvaNume($fk_cote_tva_nume)
+    {
+        $this->fk_cote_tva_nume = $fk_cote_tva_nume;
+    }
+
+    public function getFkCoteTvaNume()
+    {
+        return $this->fk_cote_tva_nume;
+    }
+
+    public function setProdTip($prod_tip)
+    {
+        $this->prod_tip = $prod_tip;
+    }
+
+    public function getProdTip()
+    {
+        return $this->prod_tip;
+    }
+
+    public function setProdPretFtva($prod_pret_ftva)
+    {
+        $this->prod_pret_ftva = $prod_pret_ftva;
+    }
+
+    public function getProdPretFtva()
+    {
+        return $this->prod_pret_ftva;
+    }
+
+    public function setProdPretCtva($prod_pret_ctva)
+    {
+        $this->prod_pret_ctva = $prod_pret_ctva;
+    }
+
+    public function getProdPretCtva()
+    {
+        return $this->prod_pret_ctva;
+    }
+
+}
+
+?>

+ 364 - 0
Core/App/Entity/Proforma.php

@@ -0,0 +1,364 @@
+<?php
+
+namespace Core\App\Entity;
+
+class Proforma
+{
+    public $facturi_key;
+    public $facturi_punct_de_lucru;
+    public $facturi_gestiune;
+    public $facturi_data;
+    public $facturi_data_scadenta;
+    public $facturi_cota_tva;
+    public $facturi_moneda;
+    public $facturi_serie;
+    public $facturi_numar;
+    public $facturi_obs_up;
+    public $facturi_mod_plata;
+    public $facturi_status;
+    public $facturi_client_id;
+    public $facturi_nume_client;
+    public $facturi_tip_persoana;
+    public $facturi_codf_client;
+    public $facturi_nrreg_client;
+    public $facturi_sediu_client;
+    public $facturi_judet_client;
+    public $facturi_oras_client;
+    public $facturi_clienti_tel;
+    public $facturi_email_client;
+    public $facturi_clienti_adresa_livrare;
+    public $facturi_cont_client;
+    public $facturi_banca_client;
+    public $facturi_obs_client;
+    public $facturi_clienti_oras_livrare;
+    public $facturi_clienti_judet_livrare;
+
+    public $facturi_nume_delegat;
+    public $facturi_act_delegat;
+    public $facturi_obs_delegat;
+    public $fk_agent_name;
+    public $unique_id_insert;
+
+    public function getFacturiKey()
+    {
+        return $this->facturi_key;
+    }
+
+    public function getFacturiPunctDeLucru()
+    {
+        return $this->facturi_punct_de_lucru;
+    }
+
+    public function getFacturiGestiune()
+    {
+        return $this->facturi_gestiune;
+    }
+
+    public function getFacturiData()
+    {
+        return $this->facturi_data;
+    }
+    public function getFacturiDataScadenta()
+    {
+        return $this->facturi_data_scadenta;
+    }
+
+    public function getFacturiCotaTva()
+    {
+        return $this->facturi_cota_tva;
+    }
+
+    public function getFacturiMoneda()
+    {
+        return $this->facturi_moneda;
+    }
+
+    public function getFacturiSerie()
+    {
+        return $this->facturi_serie;
+    }
+    public function getFacturiNumar()
+    {
+        return $this->facturi_numar;
+    }
+
+    public function getFacturiObsUp()
+    {
+        return $this->facturi_obs_up;
+    }
+
+    public function getFacturiModPlata()
+    {
+        return $this->facturi_mod_plata;
+    }
+
+    public function getFacturiStatus()
+    {
+        return $this->facturi_status;
+    }
+    public function getFacturiClientId()
+    {
+        return $this->facturi_client_id;
+    }
+
+    public function getFacturiNumeClient()
+    {
+        return $this->facturi_nume_client;
+    }
+
+    public function getFacturiTipPersoana()
+    {
+        return $this->facturi_tip_persoana;
+    }
+
+    public function getFacturiCodfClient()
+    {
+        return $this->facturi_codf_client;
+    }
+
+    public function getFacturiNrregClient()
+    {
+        return $this->facturi_nrreg_client;
+    }
+
+    public function getFacturiSediuClient()
+    {
+        return $this->facturi_sediu_client;
+    }
+
+    public function getFacturiJudetClient()
+    {
+        return $this->facturi_judet_client;
+    }
+
+    public function getFacturiOrasClient()
+    {
+        return $this->facturi_oras_client;
+    }
+
+    public function getFacturiClientiTel()
+    {
+        return $this->facturi_clienti_tel;
+    }
+
+    public function getFacturiEmailClient()
+    {
+        return $this->facturi_email_client;
+    }
+
+    public function getFacturiClientiAdresaLivrare()
+    {
+        return $this->facturi_clienti_adresa_livrare;
+    }
+
+    public function getFacturiContClient()
+    {
+        return $this->facturi_cont_client;
+    }
+
+    public function getFacturiBancaClient()
+    {
+        return $this->facturi_banca_client;
+    }
+
+    public function getFacturiObsClient()
+    {
+        return $this->facturi_obs_client;
+    }
+
+    public function getFacturiClientiOrasLivrare()
+    {
+        return $this->facturi_clienti_oras_livrare;
+    }
+
+    public function getFacturiClientiJudetLivrare()
+    {
+        return $this->facturi_clienti_judet_livrare;
+    }
+
+    public function getFacturiNumeDelegat()
+    {
+        return $this->facturi_nume_delegat;
+    }
+    public function getFacturiActDelegat()
+    {
+        return $this->facturi_act_delegat;
+    }
+
+    public function getFacturiObsDelegat()
+    {
+        return $this->facturi_obs_delegat;
+    }
+
+    public function getFkAgentName()
+    {
+        return $this->fk_agent_name;
+    }
+
+    public function setFacturiKey($facturi_key)
+    {
+        $this->facturi_key = $facturi_key;
+    }
+
+    public function setFacturiPunctDeLucru($facturi_punct_de_lucru)
+    {
+        $this->facturi_punct_de_lucru = $facturi_punct_de_lucru;
+    }
+
+    public function setFacturiGestiune($facturi_gestiune)
+    {
+        $this->facturi_gestiune = $facturi_gestiune;
+    }
+
+    public function setFacturiData($facturi_data)
+    {
+        $this->facturi_data = $facturi_data;
+    }
+    public function setFacturiDataScadenta($facturi_data_scadenta)
+    {
+        $this->facturi_data_scadenta = $facturi_data_scadenta;
+    }
+
+    public function setFacturiCotaTva($facturi_cota_tva)
+    {
+        $this->facturi_cota_tva = $facturi_cota_tva;
+    }
+
+    public function setFacturiMoneda($facturi_moneda)
+    {
+        $this->facturi_moneda = $facturi_moneda;
+    }
+
+    public function setFacturiSerie($facturi_serie)
+    {
+        $this->facturi_serie = $facturi_serie;
+    }
+    public function setFacturiNumar($facturi_numar)
+    {
+        $this->facturi_numar = $facturi_numar;
+    }
+
+    public function setFacturiObsUp($facturi_obs_up)
+    {
+        $this->facturi_obs_up = $facturi_obs_up;
+    }
+
+    public function setFacturiModPlata($facturi_mod_plata)
+    {
+        $this->facturi_mod_plata = $facturi_mod_plata;
+    }
+
+    public function setFacturiStatus($facturi_status)
+    {
+        $this->facturi_status = $facturi_status;
+    }
+    public function setFacturiClientId($facturi_client_id)
+    {
+        $this->facturi_client_id = $facturi_client_id;
+    }
+
+    public function setFacturiNumeClient($facturi_nume_client)
+    {
+        $this->facturi_nume_client = $facturi_nume_client;
+    }
+
+    public function setFacturiTipPersoana($facturi_tip_persoana)
+    {
+        $this->facturi_tip_persoana = $facturi_tip_persoana;
+    }
+
+    public function setFacturiCodfClient($facturi_codf_client)
+    {
+        $this->facturi_codf_client = $facturi_codf_client;
+    }
+
+    public function setFacturiNrregClient($facturi_nrreg_client)
+    {
+        $this->facturi_nrreg_client = $facturi_nrreg_client;
+    }
+
+    public function setFacturiSediuClient($facturi_sediu_client)
+    {
+        $this->facturi_sediu_client = $facturi_sediu_client;
+    }
+
+    public function setFacturiJudetClient($facturi_judet_client)
+    {
+        $this->facturi_judet_client = $facturi_judet_client;
+    }
+
+    public function setFacturiOrasClient($facturi_oras_client)
+    {
+        $this->facturi_oras_client = $facturi_oras_client;
+    }
+
+    public function setFacturiClientiTel($facturi_clienti_tel)
+    {
+        $this->facturi_clienti_tel = $facturi_clienti_tel;
+    }
+
+    public function setFacturiEmailClient($facturi_email_client)
+    {
+        $this->facturi_email_client = $facturi_email_client;
+    }
+
+    public function setFacturiClientiAdresaLivrare($facturi_clienti_adresa_livrare)
+    {
+        $this->facturi_clienti_adresa_livrare = $facturi_clienti_adresa_livrare;
+    }
+
+    public function setFacturiContClient($facturi_cont_client)
+    {
+        $this->facturi_cont_client = $facturi_cont_client;
+    }
+
+    public function setFacturiBancaClient($facturi_banca_client)
+    {
+        $this->facturi_banca_client = $facturi_banca_client;
+    }
+
+    public function setFacturiObsClient($facturi_obs_client)
+    {
+        $this->facturi_obs_client = $facturi_obs_client;
+    }
+
+    public function setFacturiClientiOrasLivrare($facturi_clienti_oras_livrare)
+    {
+        $this->facturi_clienti_oras_livrare = $facturi_clienti_oras_livrare;
+    }
+
+    public function setFacturiClientiJudetLivrare($facturi_clienti_judet_livrare)
+    {
+        $this->facturi_clienti_judet_livrare = $facturi_clienti_judet_livrare;
+    }
+
+    public function setFacturiNumeDelegat($facturi_nume_delegat)
+    {
+        $this->facturi_nume_delegat = $facturi_nume_delegat;
+    }
+    public function setFacturiActDelegat($facturi_act_delegat)
+    {
+        $this->facturi_act_delegat = $facturi_act_delegat;
+    }
+
+    public function setFacturiObsDelegat($facturi_obs_delegat)
+    {
+        $this->facturi_obs_delegat = $facturi_obs_delegat;
+    }
+
+    public function setFkAgentName($fk_agent_name)
+    {
+        $this->fk_agent_name = $fk_agent_name;
+    }
+
+    public function getUniqueIdInsert()
+    {
+        $this->unique_id_insert;
+    }
+
+    public function setUniqueIdInsert($unique_id_insert)
+    {
+        $this->unique_id_insert = $unique_id_insert;
+    }
+    
+}

+ 8 - 0
Core/App/Entity/ProformaItem.php

@@ -0,0 +1,8 @@
+<?php
+
+namespace Core\App\Entity;
+
+class ProformaItem extends OrderItem
+{
+
+}

+ 245 - 0
Core/App/Entity/Settings.php

@@ -0,0 +1,245 @@
+<?php
+
+namespace Core\App\Entity;
+
+class Settings
+{
+    public $auth_api_key;
+    public $auth_username;
+    public $auth_password;
+    public $auth_fiscal_code;
+
+    public $option_auto_stock_sync;
+    public $option_auto_order_sync;
+    public $option_stock_filter;
+    public $option_order_or_proforma;
+    public $option_proforma_serie;
+    public $option_ordered_days_ago;
+    public $option_locations;
+
+    public $custom_field_bank;
+    public $custom_field_account;
+    public $custom_field_company;
+    public $custom_field_fiscal_code;
+    public $custom_field_reg_number;
+
+    public $auto_sync_options;
+    public $sync_data_types;
+    public $order_or_proforma_options;
+    public $sync_stock_filter_options;
+	public $option_with_discount;
+    public function getAuthApiKey()
+    {
+        return $this->auth_api_key;
+    }
+
+    public function getAuthUsername()
+    {
+        return $this->auth_username;
+    }
+
+    public function getAuthPassword()
+    {
+        return hex2bin($this->auth_password);
+    }
+
+    public function getAuthFiscalCode()
+    {
+        return $this->auth_fiscal_code;
+    }
+
+    public function getOptionAutoStockSync()
+    {
+        return $this->option_auto_stock_sync;
+    }
+
+    public function getOptionAutoOrderSync()
+    {
+        return $this->option_auto_order_sync;
+    }
+
+    public function getOptionStockFilter()
+    {
+        return $this->option_stock_filter;
+    }
+
+    public function getOptionOrderOrProforma()
+    {
+        return $this->option_order_or_proforma;
+    }
+
+    public function getOptionProformaSerie()
+    {
+        return $this->option_proforma_serie;
+    }
+
+    public function getOptionOrderedDaysAgo()
+    {
+        return $this->option_ordered_days_ago;
+    }
+
+    public function getOptionLocations()
+    {
+        return $this->option_locations;
+    }
+
+    public function getCustomFieldBank()
+    {
+        return $this->custom_field_bank;
+    }
+
+    public function getCustomFieldAccount()
+    {
+        return $this->custom_field_account;
+    }
+
+    public function getCustomFieldCompany()
+    {
+        return $this->custom_field_company;
+    }
+
+    public function getCustomFieldFiscalCode()
+    {
+        return $this->custom_field_fiscal_code;
+    }
+
+    public function getCustomFieldRegNumber()
+    {
+        return $this->custom_field_reg_number;
+    }
+
+    public function setAuthApiKey($auth_api_key)
+    {
+        $this->auth_api_key = $auth_api_key;
+    }
+
+    public function setAuthUsername($auth_username)
+    {
+        $this->auth_username = $auth_username;
+    }
+
+    public function setAuthPassword($auth_password)
+    {
+        $this->auth_password = $auth_password;
+    }
+
+    public function setAuthFiscalCode($auth_fiscal_code)
+    {
+        $this->auth_fiscal_code = $auth_fiscal_code;
+    }
+
+    public function setOptionAutoStockSync($option_auto_stock_sync)
+    {
+        $this->option_auto_stock_sync = $option_auto_stock_sync;
+    }
+
+    public function setOptionAutoOrderSync($option_auto_order_sync)
+    {
+        $this->option_auto_order_sync = $option_auto_order_sync;
+    }
+
+    public function setOptionStockFilter($option_stock_filter)
+    {
+        $this->option_stock_filter = $option_stock_filter;
+    }
+
+    public function setOptionOrderOrProforma($option_order_or_proforma)
+    {
+        $this->option_order_or_proforma = $option_order_or_proforma;
+    }
+
+    public function setOptionProformaSerie($option_proforma_serie)
+    {
+        $this->option_proforma_serie = $option_proforma_serie;
+    }
+
+    public function setOptionOrderedDaysAgo($option_ordered_days_ago)
+    {
+        $this->option_ordered_days_ago = $option_ordered_days_ago;
+    }
+
+    public function setOptionLocations($option_locations)
+    {
+        $this->option_locations = $option_locations;
+    }
+
+    public function setCustomFieldBank($custom_field_bank)
+    {
+        $this->custom_field_bank = $custom_field_bank;
+    }
+
+    public function setCustomFieldAccount($custom_field_account)
+    {
+        $this->custom_field_account = $custom_field_account;
+    }
+
+    public function setCustomFieldCompany($custom_field_company)
+    {
+        $this->custom_field_company = $custom_field_company;
+    }
+
+    public function setCustomFieldFiscalCode($custom_field_fiscal_code)
+    {
+        $this->custom_field_fiscal_code = $custom_field_fiscal_code;
+    }
+
+    public function setCustomFieldRegNumber($custom_field_reg_number)
+    {
+        $this->custom_field_reg_number = $custom_field_reg_number;
+    }
+
+    public function getAutoSyncOptions()
+    {
+        return $this->auto_sync_options;
+    }
+
+    public function setAutoSyncOptions($auto_sync_options)
+    {
+        $this->auto_sync_options = $auto_sync_options;
+    }
+
+    public function getOrderOrProformaOptions()
+    {
+        return $this->order_or_proforma_options;
+    }
+
+    public function setOrderOrProformaOptions($order_or_proforma_options)
+    {
+        $this->order_or_proforma_options = $order_or_proforma_options;
+    }
+
+    public function getSyncDataTypes()
+    {
+        return $this->sync_data_types;
+    }
+
+    public function setSyncDataTypes($sync_data_types)
+    {
+        $this->sync_data_types = $sync_data_types;
+    }
+
+    public function getSyncStockFilterOptions()
+    {
+        return $this->sync_stock_filter_options;
+    }
+
+    public function setSyncStockFilterOptions($sync_stock_filter_options)
+    {
+        $this->sync_stock_filter_options = $sync_stock_filter_options;
+    }
+
+	public function getOptionWithDiscount()
+    {
+        return $this->option_with_discount;
+    }
+
+    public function setOptionWithDiscount($option_with_discount)
+    {
+        $this->option_with_discount = $option_with_discount;
+    }
+
+}
+
+   
+    
+

+ 40 - 0
Core/App/Entity/Stock.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace Core\App\Entity;
+
+class Stock
+{
+    public $identifier_name;
+    public $identifier_value;
+    public $qty;
+
+    public function getIdentifierName()
+    {
+        return $this->identifier_name;
+    }
+
+    public function getIdentifierValue()
+    {
+        return $this->identifier_value;
+    }
+
+    public function getQty()
+    {
+        return $this->qty;
+    }
+
+    public function setIdentifierName($identifier_name)
+    {
+        $this->identifier_name = $identifier_name;
+    }
+
+    public function setIdentifierValue($identifier_value)
+    {
+        $this->identifier_value = $identifier_value;
+    }
+
+    public function setQty($qty)
+    {
+        $this->qty = $qty;
+    }
+}

+ 54 - 0
Core/App/Factory/EntityFactory.php

@@ -0,0 +1,54 @@
+<?php 
+
+namespace Core\App\Factory;
+
+class EntityFactory
+{
+    public $prefix;
+    const UM = 'BUC';
+    
+    public function stripSpecialChars($string)
+	{
+		$string = str_replace(array("\n", "\r", "&nbsp;"), array('', '', ''), $string);
+		$string = preg_replace('/[%&\'"]/', '', $string);
+		$string = html_entity_decode($string, ENT_COMPAT, 'UTF-8');
+        //$string = preg_replace( '/[^[:print:]]/', '',$string);
+
+        return $string;
+    }
+
+    public function setPrefix($prefix)
+    {
+        $this->prefix = $prefix;
+    }
+
+    public function getPrefix()
+    {
+        return $this->prefix;
+    }
+
+    public function getTaxRate($taxRate)
+    {
+        if(empty($taxRate) || !is_numeric($taxRate))
+        {
+            return 0;
+        }
+
+        if($taxRate < 1)
+        {
+            $tax = $taxRate * 100;   
+            $tax = (int) $tax;  
+        }
+        else
+        {
+            $tax = (int) $taxRate;
+        }
+
+        return $tax;
+    }
+
+    public function getTaxName($taxRate)
+    {
+        return $this->getTaxRate($taxRate) . '%';
+    }
+}

+ 162 - 0
Core/App/Factory/OrderFactory.php

@@ -0,0 +1,162 @@
+<?php 
+
+namespace Core\App\Factory;
+
+class OrderFactory extends EntityFactory
+{
+
+    public function createOrder(
+        $order_id, 
+        $created_date, 
+        $currency,
+
+        $customer_id,
+        $customer_first_name,
+        $customer_last_name,
+        $billing_company,
+        $email,
+
+        $billing_address_first_name,
+        $billing_address_last_name,
+        $billing_address_address1,
+        $billing_address_address2,
+        $billing_address_zip,
+        $billing_address_city,
+        $billing_address_province,
+        $billing_address_phone,
+        
+        $shipping_address_first_name,
+        $shipping_address_last_name,
+        $shipping_address_address1,
+        $shipping_address_address2,
+        $shipping_address_zip,
+        $shipping_address_city,
+        $shipping_address_province,
+        $shipping_address_phone,
+
+        $order_status,
+        $payment_method,
+        $comments,
+        $fiscal_code,
+        $company,
+        $nr_inreg
+    )
+    {
+  
+        $order = new \Core\App\Entity\Order();
+
+        $order->setFacturiIdExtern($order_id);
+        $order->setFacturiNumar($order_id);
+        $order->setFacturiData(date('Y-m-d H:i:s', strtotime($created_date)));
+        $order->setFacturiMoneda($currency);
+
+        $order->setFacturiClientId($customer_id);
+        $order->setFacturiNumeClient($this->getPaymentName($customer_first_name, $customer_last_name, $billing_address_first_name, $billing_address_last_name, $billing_company, $company));
+        $order->setFacturiTipPersoana(!empty($fiscal_code) ? 'juridica' : 'fizica');
+        $order->setFacturiCodfClient($fiscal_code);
+        $order->setFacturiNrregClient($nr_inreg);
+        $order->setFacturiEmailClient($email);
+
+        $order->setFacturiSediuClient($this->getPaymentAddress($billing_address_address1, $billing_address_address2, $billing_address_zip));
+        $order->setFacturiOrasClient($this->stripSpecialChars($billing_address_city));
+        $order->setFacturiJudetClient($this->stripSpecialChars($billing_address_province));    
+        $order->setFacturiTelClient($billing_address_phone);
+        
+        $order->setFacturiNumeLivrare($this->getShippingName($shipping_address_first_name, $shipping_address_last_name));
+        $order->setFacturiClientiAdresaLivrare($this->getShippingAddress($shipping_address_address1, $shipping_address_address2, $shipping_address_zip));
+        $order->setFacturiCodpostalLivrare($shipping_address_zip);
+        $order->setFacturiOrasLivrare($this->stripSpecialChars($shipping_address_city));
+        $order->setFacturiJudetLivrare($this->stripSpecialChars($shipping_address_province));
+        $order->setFacturiTelLivrare($this->getShippingPhone($shipping_address_phone, $billing_address_phone));
+
+        $order->setFacturiStatus($order_status);
+        $order->setFacturiModPlata($payment_method);
+        $order->setFacturiObs($this->getObs($order_id, $shipping_address_first_name, $shipping_address_last_name, $shipping_address_phone, $billing_address_phone, $comments));
+
+        return $order;
+    }
+
+    protected function getPaymentName($customer_first_name, $customer_last_name, $billing_address_first_name, $billing_address_last_name, $billing_company, $company)
+    {
+        $name = $customer_first_name . ' ' . $customer_last_name;
+        
+        $billing_name = $billing_address_first_name . ' ' . $billing_address_last_name;
+        $billing_name = trim($billing_name);
+        if(!empty($billing_name))
+        {
+            $name = $billing_name;
+        }
+
+        if(!empty($billing_company))
+        {
+            $name = $billing_company; 
+        }
+
+        if(!empty($company))
+        {
+            $name = $company;
+        }
+
+        return  $this->stripSpecialChars($name);
+    }
+
+  
+    protected function getPaymentAddress($billing_address_address1, $billing_address_address2, $billing_address_zip)
+    {
+        $address = array();
+        $address[] = $billing_address_address1 . ' ' . $billing_address_address2;
+        $address[] = $billing_address_zip;
+       
+        return $this->stripSpecialChars(implode(' ', $address));
+
+    }
+
+    protected function getShippingAddress($shipping_address_address1, $shipping_address_address2, $shipping_address_zip)
+    {
+        $address = array();
+        $address[] = $shipping_address_address1;
+        $address[] = $shipping_address_address2;
+        $address[] = $shipping_address_zip;
+       
+        return $this->stripSpecialChars(implode(' ', $address));
+
+    }
+
+    protected function getShippingName($shipping_address_first_name, $shipping_address_last_name)
+    {
+        $result = $this->stripSpecialChars($shipping_address_first_name . ' ' . $shipping_address_last_name);
+
+        return $result;
+    }
+
+    protected function getShippingPhone($shipping_address_phone, $billing_address_phone)
+    {
+        if(!empty($shipping_address_phone))
+        {
+            return $shipping_address_phone;
+        }
+        else
+        {
+           return $billing_address_phone;
+        }
+    }
+
+    protected function getObs($order_id, $shipping_address_first_name, $shipping_address_last_name, $shipping_address_phone, $billing_address_phone, $comments)
+    {
+        $output = array();
+        $output[] = 'Comanda nr: ' . $order_id;
+        $output[] = 'Nume si prenume la livrare: ' . $this->getShippingName($shipping_address_first_name, $shipping_address_last_name);
+        $output[] = 'Telefon la livrare: ' . $this->getShippingPhone($shipping_address_phone, $billing_address_phone);
+
+        if(empty($comments))
+        {
+            $comments = '-';
+        }
+        
+        $output[] = 'Comentarii la comanda: ' . $this->stripSpecialChars($comments);
+
+        return implode('; ', $output);
+    }
+
+    
+}

+ 249 - 0
Core/App/Factory/OrderItemFactory.php

@@ -0,0 +1,249 @@
+<?php 
+
+namespace Core\App\Factory;
+
+class OrderItemFactory extends EntityFactory
+{
+    public $remoteProducts;
+	public $localProducts;
+    public $withDiscountOption;
+
+    public function createOrderProduct(
+        $product_ids,
+        $product_name,
+        $ean,
+        $sku,
+
+        $currency_code,
+        $price,
+        $taxRate,
+        $quantity,
+        $tax = 0,
+        $taxIncluded = false
+    )
+    {
+        $orderProduct = new \Core\App\Entity\OrderItem();
+
+        $prod_cod1 = $this->getPrefix().$product_ids;
+        $taxRate = $this->getTaxRate($taxRate) ;
+
+        $initialPrice = $price;
+        $initialTax = $tax;
+        $price = $this->calculatePrice($price, $product_name, $ean, $sku, $prod_cod1);
+        $tax = $this->calculateTax($price, $tax, $taxRate);
+
+        if(!empty($prod_cod1) && in_array($prod_cod1, array_column($this->remoteProducts, 'Alt_Cod')))
+        {
+            $index = array_search($prod_cod1, array_column($this->remoteProducts, 'Alt_Cod'));
+        }
+
+        if(!empty($ean) && in_array($ean, array_column($this->remoteProducts, 'Cod_EAN')))
+        {
+            $index = array_search($ean, array_column($this->remoteProducts, 'Cod_EAN'));
+        }
+
+        if(!empty($sku) && in_array($sku, array_column($this->remoteProducts, 'Cod_SKU')))
+        {
+            $index = array_search($sku, array_column($this->remoteProducts, 'Cod_SKU'));
+        }
+
+        if(!empty($product_name) && in_array($product_name, array_column($this->remoteProducts, 'Denumire')))
+        {
+            $index = array_search($product_name, array_column($this->remoteProducts, 'Denumire'));
+        }
+
+        if(isset($index))
+        {
+            $product_name = isset($this->remoteProducts[$index]['Denumire']) ? $this->remoteProducts[$index]['Denumire'] : $product_name;
+            $ean = isset($this->remoteProducts[$index]['Cod_EAN']) ? $this->remoteProducts[$index]['Cod_EAN'] : $ean;
+            $sku = isset($this->remoteProducts[$index]['Cod_SKU']) ? $this->remoteProducts[$index]['Cod_SKU'] : $sku;
+            $prod_cod1 = (isset($this->remoteProducts[$index]['Alt_Cod']) && !empty($this->remoteProducts[$index]['Alt_Cod'])) ? $this->remoteProducts[$index]['Alt_Cod'] : $prod_cod1;
+        }
+        
+        
+        $pretFtva = $this->getPretftva($price, $taxRate, $tax, $taxIncluded);
+        $pretCtva = $this->getPretctva($price, $taxRate, $tax, $taxIncluded);
+
+        $orderProduct->setFacturiProdNume($this->stripSpecialChars($product_name));
+        $orderProduct->setFacturiProdMoneda($currency_code);
+        $orderProduct->setFacturiProdPretftva($pretFtva);
+        $orderProduct->setFacturiProdPretctva($pretCtva);
+        $orderProduct->setFacturiProdTva($this->getTaxName($taxRate));
+        $orderProduct->setFacturiProdCant($quantity);
+        $orderProduct->setFacturiProdVal($pretFtva * $quantity);
+        $orderProduct->setFacturiProdValTva(($pretCtva - $pretFtva) * $quantity);
+        $orderProduct->setFacturiProdValTot($pretCtva * $quantity);
+        $orderProduct->setFacturiProdUm(self::UM);
+        $orderProduct->setProdCod($ean);
+        $orderProduct->setProdSku($sku);
+        $orderProduct->setProdCod1($prod_cod1);
+        $orderProduct->setProdCodCautare('all');  
+        
+        if($this->withDiscountOption == \Core\App\Factory\SettingsFactory::SYNC_ENABLED)
+        {
+            return array((array)$orderProduct); 
+        }
+        else
+        {
+            $discount = $initialPrice - $price;
+            if($discount < 0)
+            {
+                $tax = $tax - $initialTax;
+
+                $discountFtva = $this->getPretftva($discount, $taxRate, $tax, $taxIncluded);
+                $discountCtva = $this->getPretctva($discount, $taxRate, $tax, $taxIncluded);
+
+                if($tax == 0)
+                {
+                    $tax = $discountCtva - $discountFtva;
+                }
+
+                $orderProductDiscount = new \Core\App\Entity\OrderItem();
+                $orderProductDiscount->setFacturiProdNume('Discount la: ' . $this->stripSpecialChars($product_name));
+                $orderProductDiscount->setFacturiProdMoneda($currency_code);
+                $orderProductDiscount->setFacturiProdPretftva($discountFtva);
+                $orderProductDiscount->setFacturiProdPretctva($discountCtva);
+                $orderProductDiscount->setFacturiProdTva($taxRate . '%');
+                $orderProductDiscount->setFacturiProdCant($quantity);
+                $orderProductDiscount->setFacturiProdVal($discountFtva * $quantity);
+                $orderProductDiscount->setFacturiProdValTva($tax * $quantity);
+                $orderProductDiscount->setFacturiProdValTot($discountCtva * $quantity);
+                $orderProductDiscount->setProdCodCautare('all'); 
+        
+                return array((array)$orderProduct, (array)$orderProductDiscount); 
+            }
+            else
+            {
+                return array((array)$orderProduct); 
+            }
+        }  
+    }
+
+    public function createOrderTax(
+        $product_name,
+        $currency_code,
+        $price,
+        $tax = 0
+    )
+    {
+
+        if($price != 0)
+        {
+        $taxValue = round(($tax * 100)/$price);
+        }
+        else
+        {
+            $taxValue = 0;
+        }
+
+        $orderTax = new \Core\App\Entity\OrderItem();
+
+        $orderTax->setFacturiProdNume($this->stripSpecialChars($product_name));
+        $orderTax->setFacturiProdMoneda($currency_code);
+        $orderTax->setFacturiProdPretftva($price);
+        $orderTax->setFacturiProdPretctva($price + $tax);
+        $orderTax->setFacturiProdTva($taxValue . '%');
+        $orderTax->setFacturiProdCant(1);
+        $orderTax->setFacturiProdVal($price);
+        $orderTax->setFacturiProdValTva($tax);
+        $orderTax->setFacturiProdValTot($price + $tax);
+        $orderTax->setProdCodCautare('all');  
+    
+        return $orderTax;  
+        
+    }
+
+    protected function getPretftva($price, $taxRate, $tax, $taxIncluded)
+    {
+        if($taxIncluded)
+        {
+            if($tax != 0)
+            {
+                return ($price - $tax);
+            }
+            else
+            {
+                return ($price / (1 + ($taxRate/100)));
+            }
+        }
+        else
+        {
+            return $price;
+        }
+    }
+
+    protected function getPretctva($price, $taxRate, $tax, $taxIncluded)
+    {
+        if(!$taxIncluded)
+        {
+            if($tax != 0)
+            {
+                return ($price + $tax);
+            }
+            else
+            {
+                return ($price * (1 + ($taxRate/100)));
+            }
+        }
+        else
+        {
+            return $price;
+        }
+    }
+
+    public function setRemoteProducts($remoteProducts)
+    {
+        $this->remoteProducts = $remoteProducts;
+    }
+    
+    public function setLocalProducts($localProducts)
+    {
+        $this->localProducts = $localProducts;
+    }
+
+    public function setWithDiscountOption($withDiscountOption)
+    {
+        $this->withDiscountOption = $withDiscountOption;
+    }
+
+    public function calculatePrice($price, $product_name, $ean, $sku, $prod_cod1)
+    {
+        if($this->withDiscountOption == \Core\App\Factory\SettingsFactory::SYNC_ENABLED)
+        {
+            return $price;
+        }
+
+        if (!empty($product_name) && in_array($product_name, array_column($this->localProducts, 'prod_nume'))) {
+            $index = array_search($product_name, array_column($this->localProducts, 'prod_nume'));
+            $price =  $this->localProducts[$index]['prod_pret_ftva'];
+        }
+
+        if (!empty($prod_cod1) && in_array($prod_cod1, array_column($this->localProducts, 'prod_cod1'))) {
+            $index = array_search($prod_cod1, array_column($this->localProducts, 'prod_cod1'));
+            $price =  $this->localProducts[$index]['prod_pret_ftva'];
+        }
+
+        if (!empty($sku) && in_array($sku, array_column($this->localProducts, 'prod_sku'))) {
+            $index = array_search($sku, array_column($this->localProducts, 'prod_sku'));
+            $price =  $this->localProducts[$index]['prod_pret_ftva'];
+        }
+
+        if (!empty($ean) && in_array($ean, array_column($this->localProducts, 'prod_cod'))) {
+            $index = array_search($sku, array_column($this->localProducts, 'prod_cod'));
+            $price =  $this->localProducts[$index]['prod_pret_ftva'];
+        }
+
+        return $price;
+    }
+    
+    public function calculateTax($price, $tax, $taxRate)
+    {
+        if($this->withDiscountOption == \Core\App\Factory\SettingsFactory::SYNC_ENABLED)
+        {
+            return $tax;
+        }
+
+        return ($price * $tax)/100;
+    }
+}   
+    

+ 39 - 0
Core/App/Factory/ProductFactory.php

@@ -0,0 +1,39 @@
+<?php 
+
+namespace Core\App\Factory;
+
+class ProductFactory extends OrderItemFactory
+{
+    public function createProduct(
+        $product_ids,
+        $product_name,
+        $ean,
+        $sku,
+        $currency_code,
+        $price,
+        $tax,
+        $prod_tip,
+        $taxIncluded = false
+    )
+    {
+        $product = new \Core\App\Entity\Product();
+
+        $pretFtva = $this->getPretftva($price, $this->getTaxRate($tax), 0, $taxIncluded);
+        $pretCtva = $this->getPretctva($price, $this->getTaxRate($tax), 0, $taxIncluded);
+
+        $product->setProdNume($this->stripSpecialChars($product_name));
+        $product->setProdCod($ean);
+        $product->setProdSku($sku);
+        $product->setProdUm(self::UM);
+        $product->setProdCod1($this->getPrefix().$product_ids);
+        $product->setFkCursMoneda($currency_code);
+        $product->setFkCoteTvaNume($this->getTaxName($tax));
+        $product->setProdTip($prod_tip);
+        $product->setProdPretFtva($pretFtva);
+        $product->setProdPretCtva($pretCtva);
+        
+        return $product;  
+        
+    }
+}   
+    

+ 78 - 0
Core/App/Factory/ProformaFactory.php

@@ -0,0 +1,78 @@
+<?php 
+
+namespace Core\App\Factory;
+
+class ProformaFactory extends OrderFactory
+{
+    public function createProforma(
+        $facturi_serie, 
+        $order_id, 
+        $created_date, 
+        $currency,
+
+        $customer_id,
+        $customer_first_name,
+        $customer_last_name,
+        $billing_company,
+        $email,
+
+        $billing_address_first_name,
+        $billing_address_last_name,
+        $billing_address_address1,
+        $billing_address_address2,
+        $billing_address_zip,
+        $billing_address_city,
+        $billing_address_province,
+        $billing_address_phone,
+        
+        $shipping_address_first_name,
+        $shipping_address_last_name,
+        $shipping_address_address1,
+        $shipping_address_address2,
+        $shipping_address_zip,
+        $shipping_address_city,
+        $shipping_address_province,
+        $shipping_address_phone,
+
+        $payment_method,
+        $comments,
+        $fiscal_code,
+        $company,
+        $nr_inreg,
+        $facturi_cont_client,
+        $facturi_banca_client,
+        $taxRate
+    )
+    {
+  
+        $proforma = new \Core\App\Entity\Proforma();
+
+        $proforma->setFacturiData(date('Y-m-d H:i:s', strtotime($created_date)));
+        $proforma->setFacturiDataScadenta(date('Y-m-d H:i:s', strtotime($created_date)));
+        $proforma->setFacturiCotaTva($this->getTaxName($taxRate));
+        $proforma->setFacturiMoneda($currency);
+        $proforma->setFacturiSerie($facturi_serie);
+        $proforma->setFacturiObsUp($this->getObs($order_id, $shipping_address_first_name, $shipping_address_last_name, $shipping_address_phone, $billing_address_phone, $comments));
+        $proforma->setFacturiModPlata($payment_method);
+        $proforma->setFacturiStatus('Emisa');
+        $proforma->setFacturiClientId($customer_id);
+        $proforma->setFacturiNumeClient($this->getPaymentName($customer_first_name, $customer_last_name, $billing_address_first_name, $billing_address_last_name, $billing_company, $company));
+        $proforma->setFacturiTipPersoana(!empty($fiscal_code) ? 'juridica' : 'fizica');
+        $proforma->setFacturiCodfClient($fiscal_code);
+        $proforma->setFacturiNrregClient($nr_inreg);
+        $proforma->setFacturiSediuClient($this->getPaymentAddress($billing_address_address1, $billing_address_address2, $billing_address_zip));
+        $proforma->setFacturiJudetClient($this->stripSpecialChars($billing_address_province));
+        $proforma->setFacturiOrasClient($this->stripSpecialChars($billing_address_city));
+        $proforma->setFacturiClientiTel($billing_address_phone);
+        $proforma->setFacturiEmailClient($email);
+        $proforma->setFacturiClientiAdresaLivrare($this->getShippingAddress($shipping_address_address1, $shipping_address_address2, $shipping_address_zip));
+        $proforma->setFacturiContClient($facturi_cont_client);
+        $proforma->setFacturiBancaClient($facturi_banca_client);
+        $proforma->setFacturiObsClient('Id extern client: ' . $customer_id);
+        $proforma->setFacturiClientiOrasLivrare($this->stripSpecialChars($shipping_address_city));
+        $proforma->setFacturiClientiJudetLivrare($this->stripSpecialChars($shipping_address_province));
+        $proforma->setUniqueIdInsert($this->getPrefix() . $facturi_serie . '_' . $customer_id . '_' . $order_id . '_' . strtotime($created_date));
+
+        return $proforma;
+    }
+}

+ 44 - 0
Core/App/Factory/ProformaItemFactory.php

@@ -0,0 +1,44 @@
+<?php 
+
+namespace Core\App\Factory;
+
+class ProformaItemFactory extends OrderItemFactory
+{
+    public function createProformaProduct(
+        $product_ids,
+        $product_name,
+        $ean,
+        $sku,
+
+        $currency_code,
+        $price,
+        $taxRate,
+        $quantity,
+        $taxIncluded = false
+    )
+    {
+        $proformaProduct = new \Core\App\Entity\OrderItem();
+
+        $taxRate = $this->getTaxRate($taxRate) ;
+        $pretFtva = $this->getPretftva($price, $taxRate, 0, $taxIncluded);
+        $pretCtva = $this->getPretctva($price, $taxRate, 0, $taxIncluded);
+
+        $proformaProduct->setFacturiProdNume($this->stripSpecialChars($product_name));
+        $proformaProduct->setFacturiProdMoneda($currency_code);
+        $proformaProduct->setFacturiProdPretftva($pretFtva);
+        $proformaProduct->setFacturiProdPretctva($pretCtva);
+        $proformaProduct->setFacturiProdTva($this->getTaxName($taxRate));
+        $proformaProduct->setFacturiProdCant($quantity);
+        $proformaProduct->setFacturiProdVal($pretFtva * $quantity);
+        $proformaProduct->setFacturiProdValTva(($pretCtva - $pretFtva) * $quantity);
+        $proformaProduct->setFacturiProdValTot($pretCtva * $quantity);
+        $proformaProduct->setFacturiProdUm(self::UM);
+        $proformaProduct->setProdCod($ean);
+        $proformaProduct->setProdSku($sku);
+        $proformaProduct->setProdCod1($this->getPrefix().$product_ids);
+        $proformaProduct->setProdCodCautare('all');  
+        
+        return $proformaProduct;  
+        
+    }
+}

+ 100 - 0
Core/App/Factory/SettingsFactory.php

@@ -0,0 +1,100 @@
+<?php 
+
+namespace Core\App\Factory;
+
+class SettingsFactory extends EntityFactory
+{
+    const DEFAULT_AUTO_STOCK_SYNC = 'activ';
+    const DEFAULT_AUTO_ORDER_SYNC = 'activ';
+    const DEFAULT_ORDER_OR_PROFORMA = 'order';
+    const DEFAULT_PROFORMA_SERIE = '';
+    const DEFAULT_ORDERED_DAYS_AGO = '7';
+    const DEFAULT_STOCK_FILTER = '';
+
+    const AUTO_SYNC_ENABLED = 'activ';
+    const AUTO_SYNC_DISABLED = 'inactiv';
+
+    const SYNC_ENABLED = 'activ';
+    const SYNC_DISABLED = 'inactiv';
+
+    const SELECT_ORDER = 'order';
+    const SELECT_PROFORMA = 'proforma';
+    const DEFAULT_LOCATIONS = null;
+	const DEFAULT_WITH_DISCOUNT = 'activ';
+
+    public function createSettings(
+        $auth_api_key = '',
+        $auth_username = '',
+        $auth_password = '',
+        $auth_fiscal_code = '',
+
+        $option_auto_stock_sync = '',
+        $option_auto_order_sync = '',
+        $option_stock_filter = '',
+        $option_order_or_proforma = '',
+        $option_proforma_serie = '',
+        $option_ordered_days_ago = '',
+        $option_locations = null,
+        $option_with_discount = ''
+    )
+    {
+        $settings = new \Core\App\Entity\Settings();
+
+        $settings->setAuthApiKey($auth_api_key);
+        $settings->setAuthUsername($auth_username);
+        $settings->setAuthPassword($auth_password);
+        $settings->setAuthFiscalCode($auth_fiscal_code);
+
+        $settings->setOptionAutoStockSync(($option_auto_stock_sync != '') ? $option_auto_stock_sync : self::DEFAULT_AUTO_STOCK_SYNC);
+        $settings->setOptionAutoOrderSync(($option_auto_order_sync != '') ? $option_auto_order_sync : self::DEFAULT_AUTO_ORDER_SYNC);
+        $settings->setOptionStockFilter(($option_stock_filter != '') ? $option_stock_filter : self::DEFAULT_STOCK_FILTER);
+        $settings->setOptionOrderOrProforma(($option_order_or_proforma != '') ? $option_order_or_proforma : self::DEFAULT_ORDER_OR_PROFORMA);
+        $settings->setOptionProformaSerie(($option_proforma_serie != '') ? $option_proforma_serie : self::DEFAULT_PROFORMA_SERIE);
+        $settings->setOptionOrderedDaysAgo(($option_ordered_days_ago != '') ? $option_ordered_days_ago : self::DEFAULT_ORDERED_DAYS_AGO);
+        $settings->setOptionLocations(!is_null($option_locations) ? explode(", ", $option_locations) : self::DEFAULT_LOCATIONS);
+		$settings->setOptionWithDiscount(($option_with_discount != '') ? $option_with_discount : self::DEFAULT_WITH_DISCOUNT);
+
+        $settings->setCustomFieldBank(array(
+            'Banca', 
+            'Nume banca', 
+            'Numele bancii',
+            'Bancă',
+        ));
+        $settings->setCustomFieldAccount(array(
+            'Cont bancar',
+            'Contul bancii',
+            'Cont',
+            'Contul bancar',
+        ));
+        $settings->setCustomFieldCompany(array(
+            'Denumire firmă',
+            'Denumire firma',
+            'Denumirea firmei',
+            'Nume firmă',
+            'Nume firma',
+            'Firma',           
+            'Firmă'
+        ));
+        $settings->setCustomFieldFiscalCode(array(
+            'CUI',
+            'Cod Fiscal',
+            'Codul Fiscal'
+        ));
+        $settings->setCustomFieldRegNumber(array(
+            'Număr de înregistrare',
+            'Numar de inregistrare',
+        ));
+        
+        $settings->setAutoSyncOptions(array(self::AUTO_SYNC_ENABLED, self::AUTO_SYNC_DISABLED));
+        $settings->setOrderOrProformaOptions(array(self::SELECT_ORDER, self::SELECT_PROFORMA));
+        $settings->setSyncDataTypes(array(
+            'product',
+            'order',
+            'stock'
+        ));
+
+        return $settings;  
+        
+    }
+
+}

+ 23 - 0
Core/App/Factory/StockFactory.php

@@ -0,0 +1,23 @@
+<?php 
+
+namespace Core\App\Factory;
+
+class StockFactory extends EntityFactory
+{
+    public function createStock(
+        $identifier_name,
+        $identifier_value,
+        $qty
+    )
+    {
+        $stock = new \Core\App\Entity\Stock();
+
+        $stock->setIdentifierName($identifier_name);
+        $stock->setIdentifierValue($identifier_value);
+        $stock->setQty($qty);
+        
+        return $stock;  
+        
+    }
+
+}

+ 9 - 0
Core/App/Repository/Facturis/CustomerInterface.php

@@ -0,0 +1,9 @@
+<?php
+
+namespace Core\App\Repository\Facturis;
+
+interface CustomerInterface
+{
+    public function getAll();
+    
+}

+ 8 - 0
Core/App/Repository/Facturis/OrderInterface.php

@@ -0,0 +1,8 @@
+<?php
+
+namespace Core\App\Repository\Facturis;
+
+interface OrderInterface
+{
+    public function add($order);   
+}

+ 13 - 0
Core/App/Repository/Facturis/ProductInterface.php

@@ -0,0 +1,13 @@
+<?php
+
+namespace Core\App\Repository\Facturis;
+
+interface ProductInterface
+{
+    public function add(\Core\App\Entity\Product $product);
+
+    public function isNewProduct(\Core\App\Entity\Product $product); 
+
+    public function getAll();
+    
+}

+ 8 - 0
Core/App/Repository/Facturis/ProformaInterface.php

@@ -0,0 +1,8 @@
+<?php
+
+namespace Core\App\Repository\Facturis;
+
+interface ProformaInterface extends OrderInterface
+{
+    
+}

+ 22 - 0
Core/App/Repository/Facturis/StockInterface.php

@@ -0,0 +1,22 @@
+<?php
+
+namespace Core\App\Repository\Facturis;
+
+interface StockInterface
+{
+    public function getAll();
+
+    public function groupStock($data);
+ 
+    public function isValidProduct($item, $stockAttr, $productAttr);
+
+    public function addQty($item, $stockAttr, $productAttr);
+
+    public function setOptionStockFilter($optionStockFilter);
+
+    public function getPdlGestiuni();
+
+    public function testAuth();
+
+    public function setLocalProducts($localProducts);
+}

+ 31 - 0
Core/App/Repository/Marketplace/OrderInterface.php

@@ -0,0 +1,31 @@
+<?php
+
+namespace Core\App\Repository\Marketplace;
+
+interface OrderInterface
+{
+    public function setRemoteCustomers($remoteCustomers);
+
+    public function setRemoteProducts($remoteProducts);
+
+    public function getAll();
+
+    public function getOrder($orderFactory, $order);
+    
+    public function getOrderProducts($orderItemFactory, $order);
+
+    public function getOrderTaxes($orderItemFactory, $order);
+
+    public function getCodF($billing_address_company);
+
+    public function isValidOrder($order);
+
+    public function setOrderedDaysAgo($orderedDaysAgo);
+
+    public function getByOrderId($orderId);
+
+    public function setWithDiscount($withDiscount);
+
+    public function setLocalProducts($localProducts);
+    
+}

+ 9 - 0
Core/App/Repository/Marketplace/ProductInterface.php

@@ -0,0 +1,9 @@
+<?php
+
+namespace Core\App\Repository\Marketplace;
+
+interface ProductInterface
+{
+    public function getAll();
+    
+}

+ 8 - 0
Core/App/Repository/Marketplace/ProformaInterface.php

@@ -0,0 +1,8 @@
+<?php
+
+namespace Core\App\Repository\Marketplace;
+
+interface ProformaInterface extends OrderInterface
+{
+    public function setProformaSerie($proformaSerie);
+}

+ 36 - 0
Core/App/Repository/Marketplace/SettingsInterface.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace Core\App\Repository\Marketplace;
+
+interface SettingsInterface
+{
+    public function getAuthApiKey();
+    public function getAuthUsername();
+    public function getAuthPassword();
+    public function getAuthFiscalCode();
+
+    public function getOptionAutoStockSync();
+    public function getOptionAutoOrderSync();
+    public function getOptionStockFilter();
+    public function getOptionOrderOrProforma();
+    public function getOptionProformaSerie();
+    public function getOptionOrderedDaysAgo();
+    public function getOptionLocations();
+
+    public function getCustomFieldBank();
+    public function getCustomFieldAccount();
+    public function getCustomFieldCompany();
+    public function getCustomFieldFiscalCode();
+    public function getCustomFieldRegNumber();
+
+    public function getSyncDataTypes();
+    public function getSyncOptions();
+    public function getSyncAuth();
+
+    public function getVersionLink();
+    public function getVersion();
+
+    public function getSyncOrdersAs();
+    public function getAutoSyncOptions();
+
+}

+ 10 - 0
Core/App/Repository/Marketplace/StockInterface.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace Core\App\Repository\Marketplace;
+
+interface StockInterface
+{
+   public function update($remoteStock);
+   public function setStockLocations($locations);
+   public function setLocalProducts($products);
+}

+ 52 - 0
Core/App/Service/OrderService.php

@@ -0,0 +1,52 @@
+<?php 
+
+namespace Core\App\Service;
+
+class OrderService {
+
+    protected $marketplaceOrderRepository;
+    protected $marketplaceProductRepository;
+    protected $facturisOrderRepository;
+    protected $facturisCustomerRepository;
+    protected $facturisProductRepository;
+    protected $settingsRepository;
+
+    public function __construct(
+        \Core\App\Repository\Marketplace\OrderInterface $marketplaceOrderRepository,
+        \Core\App\Repository\Marketplace\ProductInterface $marketplaceProductRepository,
+        \Core\App\Repository\Facturis\CustomerInterface $facturisCustomerRepository,
+        \Core\App\Repository\Facturis\ProductInterface $facturisProductRepository,
+        \Core\App\Repository\Facturis\OrderInterface $facturisOrderRepository,
+        \Core\App\Repository\Marketplace\SettingsInterface $settingsRepository
+    ) 
+    {
+        $this->marketplaceOrderRepository = $marketplaceOrderRepository;
+        $this->marketplaceProductRepository = $marketplaceProductRepository;
+        $this->facturisOrderRepository = $facturisOrderRepository;
+        $this->facturisCustomerRepository = $facturisCustomerRepository;
+        $this->facturisProductRepository = $facturisProductRepository;
+        $this->settingsRepository = $settingsRepository;
+    }
+
+    public function sync() {
+
+        $facturisCustomers =  $this->facturisCustomerRepository->getAll();
+        $facturisProducts =  $this->facturisProductRepository->getAll();
+        $this->marketplaceOrderRepository->setRemoteCustomers($facturisCustomers);
+        $this->marketplaceOrderRepository->setRemoteProducts($facturisProducts);
+        $this->marketplaceOrderRepository->setOrderedDaysAgo($this->settingsRepository->getOptionOrderedDaysAgo());
+        $this->marketplaceOrderRepository->setWithDiscount($this->settingsRepository->getOptionWithDiscount());
+        $withOutDiscount = true;
+        $marketplaceProducts =  $this->marketplaceProductRepository->getAll($withOutDiscount);
+        $this->marketplaceOrderRepository->setLocalProducts((array) $marketplaceProducts);
+        $marketplaceOrders =  $this->marketplaceOrderRepository->getAll();
+
+        if(!empty($marketplaceOrders))
+        {
+            foreach($marketplaceOrders as $marketplaceOrder)
+            {
+                $this->facturisOrderRepository->add($marketplaceOrder);
+            }
+        }
+    }
+}

+ 31 - 0
Core/App/Service/ProductService.php

@@ -0,0 +1,31 @@
+<?php 
+
+namespace Core\App\Service;
+
+class ProductService {
+
+    protected $marketplaceProductRepository;
+    protected $facturisProductRepository;
+
+    public function __construct(
+        \Core\App\Repository\Marketplace\ProductInterface $marketplaceProductRepository,
+        \Core\App\Repository\Facturis\ProductInterface $facturisProductRepository) 
+    {
+        $this->marketplaceProductRepository = $marketplaceProductRepository;
+        $this->facturisProductRepository = $facturisProductRepository;
+    }
+
+    public function sync() {
+
+        $marketplaceProducts =  $this->marketplaceProductRepository->getAll();
+        if(!empty($marketplaceProducts))
+        {
+            foreach($marketplaceProducts as $marketplaceProduct)
+            {
+                $this->facturisProductRepository->add($marketplaceProduct);
+            }
+        }
+    }
+
+    
+}

+ 55 - 0
Core/App/Service/ProformaService.php

@@ -0,0 +1,55 @@
+<?php 
+
+namespace Core\App\Service;
+
+class ProformaService {
+
+    protected $marketplaceProformaRepository;
+    protected $marketplaceProductRepository;
+    protected $facturisProformaRepository;
+    protected $facturisCustomerRepository;
+    protected $facturisProductRepository;
+    protected $settingsRepository;
+
+    public function __construct(
+        \Core\App\Repository\Marketplace\ProformaInterface $marketplaceProformaRepository,
+        \Core\App\Repository\Marketplace\ProductInterface $marketplaceProductRepository,
+        \Core\App\Repository\Facturis\CustomerInterface $facturisCustomerRepository,
+        \Core\App\Repository\Facturis\ProductInterface $facturisProductRepository,
+        \Core\App\Repository\Facturis\ProformaInterface $facturisProformaRepository,
+        \Core\App\Repository\Marketplace\SettingsInterface $settingsRepository
+    ) 
+    {
+        $this->marketplaceProformaRepository = $marketplaceProformaRepository;
+        $this->marketplaceProductRepository = $marketplaceProductRepository;
+        $this->facturisProformaRepository = $facturisProformaRepository;
+        $this->facturisCustomerRepository = $facturisCustomerRepository;
+        $this->facturisProductRepository = $facturisProductRepository;
+        $this->settingsRepository = $settingsRepository;
+    }
+
+    public function sync() {
+
+        $facturisCustomers =  $this->facturisCustomerRepository->getAll();
+        $facturisProducts =  $this->facturisProductRepository->getAll();
+        $this->marketplaceProformaRepository->setRemoteCustomers($facturisCustomers);
+        $this->marketplaceProformaRepository->setRemoteProducts($facturisProducts);
+        $this->marketplaceProformaRepository->setOrderedDaysAgo($this->settingsRepository->getOptionOrderedDaysAgo());
+        $this->marketplaceProformaRepository->setProformaSerie($this->settingsRepository->getOptionProformaSerie());
+
+        $withOutDiscount = true;
+        $marketplaceProducts =  $this->marketplaceProductRepository->getAll($withOutDiscount);
+        $this->marketplaceProformaRepository->setLocalProducts((array) $marketplaceProducts);
+        $this->marketplaceProformaRepository->setWithDiscount($this->settingsRepository->getOptionWithDiscount());
+
+        $marketplaceProformas =  $this->marketplaceProformaRepository->getAll();
+        if(!empty($marketplaceProformas))
+        {
+            foreach($marketplaceProformas as $marketplaceProforma)
+            {
+                $result = $this->facturisProformaRepository->add($marketplaceProforma);
+                //$this->marketplaceProformaRepository->addProforma($marketplaceProforma, $result);
+            }
+        }
+    }
+}

+ 89 - 0
Core/App/Service/SettingsService.php

@@ -0,0 +1,89 @@
+<?php 
+
+namespace Core\App\Service;
+
+class SettingsService
+{
+    protected $settingsRepository;
+
+    public function __construct(
+        \Core\App\Repository\Marketplace\SettingsInterface $settingsRepository
+    ) 
+    {
+        $this->settingsRepository = $settingsRepository;
+    }  
+    
+    public function getSyncDataTypes()
+    {
+        return $this->settingsRepository->getSyncDataTypes();
+    }
+
+    public function getPdlGestiuni(\Core\App\Repository\Facturis\StockInterface $facturisStockRepository)
+    {
+        return $facturisStockRepository->getPdlGestiuni();
+    }
+
+    public function getOptions()
+    {
+        return $this->settingsRepository->getSyncOptions();
+    }
+
+    public function getAuth()
+    {
+        return $this->settingsRepository->getSyncAuth();
+    }
+
+    public function testAuth(\Core\App\Repository\Facturis\StockInterface $facturisStockRepository)
+    {
+        $facturisStockRepository->testAuth();
+    }
+
+    public function getVersionLink()
+    {
+        return $this->settingsRepository->getVersionLink();
+    }
+
+    public function getVersion()
+    {
+        return $this->settingsRepository->getVersion();
+    }
+
+    public function getSyncOrdersAs()
+    {
+        return $this->settingsRepository->getSyncOrdersAs();
+    }
+
+    public function getAutoSyncOptions()
+    {
+        return $this->settingsRepository->getAutoSyncOptions();
+    }
+
+	public function getEnabledDisabledOptions()
+    {
+        return $this->settingsRepository->getEnabledDisabledOptions();
+    }
+
+    public function showTutorial()
+    {
+        $authData =  $this->settingsRepository->getSyncAuth();
+
+        if(
+            empty($authData['fsync_auth_apikey']) ||
+            empty($authData['fsync_auth_username']) ||
+            empty($authData['fsync_auth_password']) ||
+            empty($authData['fsync_auth_fiscalcode'])
+        )
+        {
+            return true;
+        }
+        else
+        {
+            return false;
+        }
+    } 
+
+    public function checkLatestVersion($language)
+    {
+        $this->settingsRepository->checkLatestVersion($language);
+    }
+}

+ 35 - 0
Core/App/Service/StockService.php

@@ -0,0 +1,35 @@
+<?php 
+
+namespace Core\App\Service;
+
+class StockService{
+
+    protected $marketplaceStockRepository;
+    protected $facturisStockRepository;
+    protected $settingsRepository;
+    protected $marketplaceProductRepository;
+
+    public function __construct(
+        \Core\App\Repository\Marketplace\StockInterface $marketplaceStockRepository,
+        \Core\App\Repository\Marketplace\ProductInterface $marketplaceProductRepository,
+        \Core\App\Repository\Facturis\StockInterface $facturisStockRepository,
+        \Core\App\Repository\Marketplace\SettingsInterface $settingsRepository
+    ) 
+    {
+        $this->marketplaceStockRepository = $marketplaceStockRepository;
+        $this->marketplaceProductRepository = $marketplaceProductRepository;
+        $this->facturisStockRepository = $facturisStockRepository;
+        $this->settingsRepository = $settingsRepository;
+    }
+
+    public function sync() {
+
+        $this->facturisStockRepository->setOptionStockFilter($this->settingsRepository->getOptionStockFilter());
+        $this->facturisStockRepository->setLocalProducts($this->marketplaceProductRepository->getAll());
+        $facturisStock = $this->facturisStockRepository->getAll();       
+        $this->marketplaceStockRepository->setStockLocations($this->settingsRepository->getOptionLocations());
+        $this->marketplaceStockRepository->setLocalProducts($this->marketplaceProductRepository->getAll());
+        $this->marketplaceStockRepository->update($facturisStock);
+    }
+
+}

+ 16 - 0
Core/FacturisRepository/CustomerRepository.php

@@ -0,0 +1,16 @@
+<?php 
+
+namespace Core\FacturisRepository;
+
+class CustomerRepository 
+extends Repository
+implements \Core\App\Repository\Facturis\CustomerInterface
+{
+    public function getAll()
+    {
+        $response = $this->getCustomers();
+
+        return $response;
+    }
+
+}

+ 14 - 0
Core/FacturisRepository/OrderRepository.php

@@ -0,0 +1,14 @@
+<?php 
+
+namespace Core\FacturisRepository;
+
+class OrderRepository 
+extends Repository
+implements \Core\App\Repository\Facturis\OrderInterface
+{
+    public function add($order)
+    {
+        $response = $this->insertOrder($order);
+    }
+
+}

+ 56 - 0
Core/FacturisRepository/ProductRepository.php

@@ -0,0 +1,56 @@
+<?php 
+
+namespace Core\FacturisRepository;
+
+class ProductRepository 
+extends Repository
+implements \Core\App\Repository\Facturis\ProductInterface
+{
+
+    public $products;
+    public function __construct($apiKey, $username, $password, $fiscalCode)
+    {
+        parent::__construct($apiKey, $username, $password, $fiscalCode);
+
+        $this->products = $this->getProducts();
+    }
+
+    public function add(\Core\App\Entity\Product $product)
+    {
+        if($this->isNewProduct($product))
+        {
+            $product = (array)$product;
+            $response = $this->insertProduct($product);
+
+            if(isset($response['warning']) && $response['warning'] && isset($response['msg']))
+            {
+                throw new \Exception($response['msg']);
+            }
+        }
+    }
+
+    public function isNewProduct(\Core\App\Entity\Product $product)
+    {
+        if(!empty($product->getProdCod1()) && in_array($product->getProdCod1(), array_column($this->products, 'Alt_Cod')))
+            return false;
+
+        if(!empty($product->getProdCod()) && in_array($product->getProdCod(), array_column($this->products, 'Cod_EAN')))
+            return false;
+
+        if(!empty($product->getProdSku()) && in_array($product->getProdSku(), array_column($this->products, 'Cod_SKU')))
+            return false;
+
+        if(!empty($product->getProdNume()) && in_array($product->getProdNume(), array_column($this->products, 'Denumire')))
+            return false;
+
+        return true;     
+    }
+
+    public function getAll()
+    {
+        $response = $this->getProducts();
+
+        return $response;
+    }
+
+}

+ 14 - 0
Core/FacturisRepository/ProformaRepository.php

@@ -0,0 +1,14 @@
+<?php 
+
+namespace Core\FacturisRepository;
+
+class ProformaRepository 
+extends Repository
+implements \Core\App\Repository\Facturis\ProformaInterface
+{
+    public function add($proforma)
+    {
+        $response = $this->insertProforma($proforma);
+    }
+
+}

+ 241 - 0
Core/FacturisRepository/Repository.php

@@ -0,0 +1,241 @@
+<?php 
+
+namespace Core\FacturisRepository;
+
+class Repository
+{
+    const URL = "https://api.facturis-online.ro/api/";
+    const CONNECTTIMEOUT = "5";
+    const TIMEOUT = "300";
+
+    public $apiKey;
+    public $username;
+    public $password;
+    public $fiscalCode;
+
+    public function __construct($apiKey, $username, $password, $fiscalCode)
+    {
+        $this->apiKey = $apiKey;
+        $this->username = $username;
+        $this->password = $password;
+        $this->fiscalCode = $fiscalCode;
+    }
+
+    private function getResponse($method, $action, $params = array())
+    {
+        if(
+            empty($this->apiKey) ||
+            empty($this->username) ||
+            empty($this->password) ||
+            empty($this->fiscalCode))
+        {
+            throw new \Exception('error_auth_data');
+        }
+
+        $this->connect();
+
+        $fields = array();
+        $fields['APIkey'] = $this->apiKey;
+        $fields['u'] = $this->username;
+        $fields['p'] = $this->password;
+        $fields['c'] = $this->fiscalCode;
+        $fields['met'] = $method;
+        $fields['act'] = $action;
+        
+        $postFields = array_merge($fields, $params);
+
+        curl_setopt($this->connection,CURLOPT_POSTFIELDS, "json=" . json_encode($postFields));
+
+        \Core\Log\FileLog::write('Facturis Request: ' . json_encode($params), \Core\Log\FileLog::DATA); 
+
+		$response = curl_exec( $this->connection );
+
+        $curl_error = curl_error($this->connection);
+        $this->close();
+
+		if($curl_error)
+		{
+            throw new \Exception($this->printException(json_encode($curl_error)));
+        }
+
+		$output = json_decode($response, true);
+        if(isset($output['error']))
+        {
+            throw new \Exception($this->printException(json_encode($output['result'])));
+        }
+			
+        if(isset($output['success']) && $output['success'] == '2000')
+        {
+            return $output['result'];
+        }
+
+        if(
+            ($method == 'Stoc' && $action == 'Get') || 
+            ($method == 'Comenzi' && $action == 'Ins') ||
+            ($method == 'Proforme' && $action == 'Ins'))
+        {
+            return $output;
+        }
+
+        throw new \Exception($this->printException('err CURL'));
+    }
+
+    private function getResponseWithoutException($method, $action, $params = array())
+    {
+        $this->connect();
+
+        $fields = array();
+        $fields['APIkey'] = $this->apiKey;
+        $fields['u'] = $this->username;
+        $fields['p'] = $this->password;
+        $fields['c'] = $this->fiscalCode;
+        $fields['met'] = $method;
+        $fields['act'] = $action;
+        
+        $postFields = array_merge($fields, $params);
+
+        curl_setopt($this->connection,CURLOPT_POSTFIELDS, "json=" . json_encode($postFields));
+		$response = curl_exec( $this->connection );
+
+        $curl_error = curl_error($this->connection);
+        $this->close();
+
+		if($curl_error)
+		{
+            return array();
+        }
+
+		$output = json_decode($response, true);
+        if(isset($output['error']))
+        {
+            return array();
+        }
+			
+        if(isset($output['success']) && $output['success'] == '2000')
+        {
+            return $output['result'];
+        }
+
+        if(($method == 'Stoc' && $action == 'Get') || ($method == 'Comenzi' && $action == 'Ins'))
+        {
+            return $output;
+        }
+
+        return array();
+    }
+
+    private function connect()
+    {
+        $this->connection = curl_init();
+
+        curl_setopt($this->connection, CURLOPT_URL, self::URL);
+        curl_setopt($this->connection, CURLOPT_POST, true);
+        curl_setopt($this->connection, CURLOPT_CONNECTTIMEOUT, self::CONNECTTIMEOUT);
+        curl_setopt($this->connection, CURLOPT_RETURNTRANSFER, true);
+        curl_setopt($this->connection, CURLOPT_HEADER, false);
+        curl_setopt($this->connection, CURLOPT_TIMEOUT, self::TIMEOUT);
+        curl_setopt($this->connection, CURLOPT_SSL_VERIFYHOST, 0);
+        curl_setopt($this->connection, CURLOPT_SSL_VERIFYPEER, 0);
+    }
+
+    private function close()
+    {
+        curl_close($this->connection);
+    }
+
+    public function getPdl()
+    {
+        $response = $this->getResponseWithoutException("Pdl", "GetSelectForUser");
+        return $response;
+    }
+
+    public function test()
+    {
+        $response = $this->getResponse("Pdl", "GetSelectForUser");
+    }
+
+    protected function getGestiuni($params)
+    {
+        $response = $this->getResponseWithoutException("Gestiuni", "GetSelect", $params);
+        return $response;
+    }
+
+    protected function insertOrder($params)
+    {
+        return $this->getResponse("Comenzi", "Ins", $params);
+    }
+
+    protected function getProducts()
+    {
+        $response = $this->getResponse("Produse", "Get", array());
+        return $response;
+    }
+
+    protected function insertProduct($product)
+    {
+        $response = $this->getResponse("Produse", "Ins", $product);
+        return $response;
+    }
+
+    protected function insertProforma($params)
+    {
+        $response = $this->getResponse("Proforme", "Ins", $params);
+        return $response;
+    }
+
+    protected function getCustomers()
+    {
+        $response = $this->getResponse("Clienti", "Get", array());
+        return $response;
+    }
+
+    protected function getStockByStockFilter($params)
+    {
+        $response = $this->getResponse("Stoc", "Get", $params);
+        return $response;
+    }
+
+    public function getPdlGestiuni()
+    {
+        $list = array();
+
+        $pedl = $this->getPdl();
+
+        if(!empty($pedl))
+        {
+            foreach($pedl as $pdl)
+            {
+                if(isset($pdl['id']))
+                {
+                    $gestiuni = $this->getGestiuni(array("pdl_curent" => 0, "pdl_key" => $pdl['id']));
+                    if(!empty($gestiuni))
+                    {
+                        foreach($gestiuni as $gestiune)
+                        {   
+                            if(isset($pdl['name']) && isset($gestiune['name']))
+                            {
+                                $list[] = array(
+                                    'id' => str_replace(" ", "_", $pdl['name']) . '__' . str_replace(" ", "_", $gestiune['name']),
+                                    'name' => $pdl['name'] . ' - ' . $gestiune['name'] 
+                                );
+                            }
+                            
+                        }
+                    }
+
+                }
+            
+                
+            } 
+        }
+
+        return $list;
+    
+    }
+
+    public function printException($message)
+    {
+        return 'Err Facturis: ' . $message . ' ';
+    }
+
+}

+ 125 - 0
Core/FacturisRepository/StockRepository.php

@@ -0,0 +1,125 @@
+<?php 
+
+namespace Core\FacturisRepository;
+
+class StockRepository 
+extends Repository
+implements \Core\App\Repository\Facturis\StockInterface
+{
+    protected $remoteStock;
+    public $optionStockFilter;
+    public $localProducts;
+
+    public function getAll()
+    {
+        $this->remoteStock = array();
+        $pdl = '';
+		$gestiune = '';
+        $filters = explode('__', $this->optionStockFilter);	
+
+		$pdl = str_replace("_", " ", $filters[0]);
+		if(isset($filters[1]))
+		{
+			$gestiune = str_replace("_", " ", $filters[1]);
+        }
+
+        $optionStockFilters = array(
+            "pdl"=> $pdl,
+            "gestiune" => $gestiune,
+            "with_zero_stock" => true
+        );
+
+        $result = $this->getStockByStockFilter($optionStockFilters);
+
+        $this->groupStock($result);
+
+        return $this->remoteStock;
+    }
+
+    public function groupStock($data)
+    {
+        if(empty($data))
+        {
+            return array();
+        }
+
+        foreach($data as $item)
+        {
+            if($item['Cantitate'] != 0)
+            {
+                if($this->isValidProduct($item, 'Alt_Cod', 'prod_cod1'))
+                {
+                    $this->addQty($item, 'Alt_Cod', 'prod_cod1');
+                }
+                else
+                {
+                    if($this->isValidProduct($item, 'Cod_EAN', 'prod_cod'))
+                    {
+                        $this->addQty($item, 'Cod_EAN', 'prod_cod');
+                    }
+                    else
+                    {
+                        if($this->isValidProduct($item, 'Cod_SKU', 'prod_sku'))
+                        {
+                            $this->addQty($item, 'Cod_SKU', 'prod_sku');
+                        }
+                        else
+                        {
+                            if($this->isValidProduct($item, 'Denumire', 'prod_nume'))
+                            {
+                                $this->addQty($item, 'Denumire', 'prod_nume');
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public function isValidProduct($item, $stockAttr, $productAttr)
+    {
+        if( 
+            !empty($item[$stockAttr]) && 
+            in_array($item[$stockAttr], array_column($this->localProducts, $productAttr)))
+        {
+            return true;
+        }
+        else
+        {
+            return false;
+        }
+    }
+
+    public function addQty($item, $stockAttr, $productAttr)
+    {
+        if(in_array($item[$stockAttr], array_column($this->remoteStock, 'product')))
+        {
+            $key = array_search($item[$stockAttr], array_column($this->remoteStock, 'product'));
+            $this->remoteStock[$key]['qty'] = $this->remoteStock[$key]['qty'] + $item['Cantitate'];
+        }
+        else
+        {
+            $this->remoteStock[] = array(
+                'product' => $item[$stockAttr],
+                'type' => $productAttr,
+                'qty' => $item['Cantitate']
+            );
+        }
+    }
+
+    public function setOptionStockFilter($optionStockFilter)
+    {
+        $this->optionStockFilter = $optionStockFilter;
+    }
+
+    public function testAuth()
+    {
+        $this->test();
+    }
+
+    public function setLocalProducts($localProducts)
+    {
+        $this->localProducts = $localProducts;
+    }
+
+}

+ 20 - 0
Core/Language/AnyLanguage.php

@@ -0,0 +1,20 @@
+<?php 
+
+namespace Core\Language;
+
+class AnyLanguage 
+{
+    public $data;
+    
+    public function get($key)
+    {
+        if(isset($this->data[$key]))
+        {
+            return $this->data[$key];
+        }
+        else
+        {
+            return $key;
+        }
+    }
+}

+ 122 - 0
Core/Language/English.php

@@ -0,0 +1,122 @@
+<?php
+namespace Core\Language;
+
+class English extends AnyLanguage
+{
+    public function __construct()
+    {
+        $this->data['home'] = 'Home';
+        $this->data['back'] = 'Back';
+        $this->data['fsync_autostock_info'] = 'Note: Automatic synchronization requires a cron job with the following URL:';
+        $this->data['title'] = 'Facturis Online Sync';
+        $this->data['description'] = 'Synchronize data between your store and your Facturis Online account.';
+        $this->data['version_text'] = 'Module Version';   
+        $this->data['version_link'] = 'https://facturis-online.ro/';  
+        $this->data['check_latest_version'] = 'Click here to find the latest version'; 
+        $this->data['version_check_error'] = 'Version check error'; 
+        $this->data['version_check_success'] = 'You have the latest version!';
+        $this->data['version_check_new'] = 'There is a newer version';
+        $this->data['version_check_link_1'] = 'For details click';
+        $this->data['version_check_link_2'] = 'here';
+        $this->data['error_auth_data'] = 'Please fill in all the fields from the authentication section of the synchronization module';
+        
+        //tutorial
+        $this->data['tutorial_title'] = 'Setup instructions';
+        $this->data['tutorial_p'] = "Let's get started by following this guide";
+        $this->data['tutorial_1'] = 'This module helps a user of the Facturis Online platform who has a Shopify store and who wants to have synchronized, in a faster and more convenient way, the products, orders and stock between the 2 online applications';
+        $this->data['tutorial_2'] = 'The first thing before using this module is the correct completion of the section Authentication with data from his account Facturis Online';
+        $this->data['tutorial_3'] = 'The second step is to set preferences in the Options section to customize subsequent synchronization processes';
+        $this->data['tutorial_4'] = 'The last step is to choose a data type from the Synchronization section and start the synchronization process';
+
+        //tab 1
+        $this->data['tab_name_1'] = 'Synchronization';
+        $this->data['tab_description_1'] = 'Select the data type you want for synchronization and then press Syncronize button';
+        $this->data['data_type'] = '[Choose data type]';
+        $this->data['product'] = 'Products';
+        $this->data['order'] = 'Orders';
+        $this->data['stock'] = 'Stock';
+        $this->data['proforma'] = 'Proformas';
+        $this->data['sync_btn'] = 'Synchronize';
+        $this->data['datatype_error'] = 'The data type is required.';
+        $this->data['sync_please_wait'] = 'Synchronizing data ... Please wait ...';
+        $this->data['authform_error'] = 'The authentication form must be completed';
+        $this->data['success_sync_prod'] = 'Successfully synchronized products!';
+        $this->data['success_sync_order'] = 'Successfully synchronized orders!';
+        $this->data['success_sync_proforma'] = 'Successfully synchronized orders! (view Proforme in Facturis Online) ';
+        $this->data['success_sync_stock'] = 'Successfully synchronized stock!';
+		$this->data['start_sync_prod'] = 'Products synchronization started..';
+        $this->data['start_sync_order'] = 'Orders synchronization started..';
+        $this->data['start_sync_proforma'] = 'Proformas synchronization started..';
+        $this->data['start_sync_stock'] = 'Stock synchronization started..';        
+        $this->data['select_text'] = 'Select sync data type';
+        
+        //tab 2
+        $this->data['tab_name_2'] = 'Authentication';
+        $this->data['tab_description_2'] = 'All fields are required';
+        $this->data['auth_api_key'] = 'API Key';
+        $this->data['auth_username'] = 'Username';
+        $this->data['auth_password'] = 'Password';
+        $this->data['auth_fiscal_code'] = 'Fiscal code';
+        $this->data['save_btn'] = 'Save';
+        $this->data['auth_apikey_error'] = 'API key is required.';
+        $this->data['auth_username_error'] = 'Username is required.';
+        $this->data['auth_password_error'] = 'Password is required.';
+        $this->data['auth_fiscalcode_error'] = 'Fiscal code is required.';
+        $this->data['success_auth'] = 'The credentials are correct and now you have access to synchronization!';
+
+        //tab 3
+        $this->data['tab_name_3'] = 'Order Options';
+        $this->data['tab_name_3_1'] = 'Order Synchronization Options';
+        $this->data['tab_name_3_2'] = 'Stock Synchronization Options';
+
+        $this->data['sync_orders_as'] = 'Synchronize orders as';
+        $this->data['proforma_series'] = 'Proforma series';
+        $this->data['days_ago'] = 'Sync only orders created in the last few days';
+        $this->data['auto_order_sync'] = 'Auto order synchronization';
+        $this->data['auto_stock_sync'] = 'Auto stock synchronization';
+        $this->data['activ'] = 'Enabled';
+        $this->data['inactiv'] = 'Disabled';
+
+        $this->data['cron_job'] = 'Note: Automatic synchronization requires a cron job with the following URL:';
+        $this->data['filter_stock'] = 'Filter stock by working unit and management';
+        $this->data['filter_stock_all'] = 'All working units';
+        $this->data['loading_data_please_wait'] = 'Loading data ... Please wait ...';
+
+        $this->data['option_proformaserie_error'] = 'Proforma serie is required';   
+        $this->data['option_daysago_error'] = 'Field is required and must contain only digits';  
+        $this->data['success_options'] = 'You have successfully saved the selected options!';
+        $this->data['success_save_data'] = 'You have successfully saved the selected data!';
+        $this->data['locations'] = 'Choose the locations where the stock will be updated';
+		$this->data['success_save_options'] = 'You have successfully saved the selected options!';
+        $this->data['success_save_auth'] = 'You have successfully saved the entered credentials!';
+        
+		$this->data['with_discount'] = 'Include discount in final price';
+        $this->data['with_discount_desc'] = '(valid for orders, invoices and proforms)';
+
+
+        //tab 4
+        $this->data['tab_name_4'] = 'Stock Options';
+        
+        //tab 5
+        $this->data['tab_name_5'] = 'Logs';
+        $this->data['datepicker'] = 'en-gb';
+        $this->data['download_btn'] = 'Download';
+        $this->data['clear_btn'] = 'Clear';
+        $this->data['tab_description_5_1'] = 'Select data interval and then press Download button to get the logs';
+        $this->data['tab_description_5_2'] = 'Press Clear to reset all logs';
+        $this->data['success_download_log'] = 'The log file was downloaded succesfully!';
+        $this->data['error_download_log'] = 'There was an error downloading the log file';
+        $this->data['download_log_error_1'] = 'You must complete the start date';
+        $this->data['download_log_error_2'] = 'The start date must be less than or equal to the end date';
+        $this->data['confirm_clear_log'] = 'Are you sure you want to clear the log file ?';
+        $this->data['success_clear_log'] = 'The log file was cleared succesfully!';
+        $this->data['error_clear_log'] = 'There was an error clearing the log file';
+        $this->data['log_header_date'] = 'Date';
+        $this->data['log_header_type'] = 'Type';
+        $this->data['log_header_description'] = 'Description';
+		$this->data['success_test_auth'] = 'Authentication testing completed successfully';
+        $this->data['start_sync_process'] = 'The sync process has started';
+        
+    }
+
+}

+ 125 - 0
Core/Language/Romanian.php

@@ -0,0 +1,125 @@
+<?php
+namespace Core\Language;
+
+class Romanian extends AnyLanguage
+{
+    public function __construct()
+    {
+        $this->data['home'] = 'Acasă';
+        $this->data['back'] = 'Înapoi';
+        $this->data['fsync_autostock_info'] = 'Nota: Pentru sincronizarea automata este nevoie de un cron job cu urmatorul URL: ';
+        $this->data['title'] = 'Facturis Online Sync';
+        $this->data['description'] = 'Sincronizați datele între magazinul dvs. și contul Facturis Online.';
+        $this->data['version_text'] = 'Versiunea modulului';   
+        $this->data['version_link'] = 'https://facturis-online.ro/';  
+        $this->data['check_latest_version'] = 'Verificați aici care este cea mai recentă versiune'; 
+        $this->data['version_check_error'] = 'Eroare la verificarea versiunii'; 
+        $this->data['version_check_success'] = 'Aveți ultima versiune!';
+        $this->data['version_check_new'] = 'Există o versiune mai nouă';
+        $this->data['version_check_link_1'] = 'Pentru detalii, accesați';
+        $this->data['version_check_link_2'] = 'aici';
+		$this->data['error_auth_data'] = 'Vă rugăm să completați toate câmpurile de la secțiunea autentificare a modulului pentru sincronizare';
+
+        //tutorial
+        $this->data['tutorial_title'] = 'Instrucțiuni';
+        $this->data['tutorial_p'] = 'Să începem urmând acest ghid';
+        $this->data['tutorial_1'] = 'Acest modul vine în ajutorul unui utilizator al platformei Facturis Online care are un magazin Shopify și care își dorește aibă sincronizate, într-un mod mai rapid și mai comod, produsele, comenzile și stocul între cele 2 aplicații online';
+        $this->data['tutorial_2'] = 'Primul lucrul înainte de a folosi acest modul reprezintă completarea corectă a secțiunii Autentificare cu date din contul său Facturis Online';
+        $this->data['tutorial_3'] = 'Al doilea pas este setarea unor preferințe din secțiunea Opțiuni pentru a particulariza procesele de sincronizare ulterioare';
+        $this->data['tutorial_4'] = 'Ultimul pas este alegerea unui tip de date din secțiunea Sincronizare și pornirea procesului de sincronizare';
+
+        //tab 1
+        $this->data['tab_name_1'] = 'Sincronizare';
+        $this->data['tab_description_1'] = 'Selectați tipul de date pe care îl doriți pentru sincronizare și apoi apăsați butonul Sincronizare';
+        $this->data['data_type'] = '[Alegeți tipul de date]';
+        $this->data['product'] = 'Produse';
+        $this->data['order'] = 'Comenzi';
+        $this->data['stock'] = 'Stoc';
+        $this->data['proforma'] = 'Proforme';
+        $this->data['sync_btn'] = 'Sincronizare';
+        $this->data['datatype_error'] = 'Tipul de date este obligatoriu.';
+        $this->data['sync_please_wait'] = 'Se sincronizează datele ... Vă rugăm să așteptați ...';
+        $this->data['authform_error'] = 'Formularul de autentificare trebuie completat';
+        $this->data['success_sync_prod'] = 'Produse sincronizate cu succes!';
+        $this->data['success_sync_order'] = 'Comenzi sincronizate cu succes!';
+        $this->data['success_sync_proforma'] = 'Comenzi sincronizate cu succes! (vizualizați Proforme în Facturis Online)';
+        $this->data['success_sync_stock'] = 'Stoc sincronizat cu succes!';
+		$this->data['start_sync_prod'] = 'S-a început sincronizarea produselor..';
+        $this->data['start_sync_order'] = 'S-a început sincronizarea comenzilor..';
+        $this->data['start_sync_proforma'] = 'S-a început sincronizarea proformelor..';
+        $this->data['start_sync_stock'] = 'S-a început sincronizarea stocului..';
+        
+        $this->data['select_text'] = 'Selectați tipul de date';
+
+        //tab 2
+        $this->data['tab_name_2'] = 'Autentificare';
+        $this->data['tab_description_2'] = 'Toate câmpurile sunt obligatorii';
+        $this->data['auth_api_key'] = 'Cheie API';
+        $this->data['auth_username'] = 'Nume utilizator';
+        $this->data['auth_password'] = 'Parolă';
+        $this->data['auth_fiscal_code'] = 'Codul fiscal';
+        $this->data['save_btn'] = 'Salvare';
+        $this->data['auth_apikey_error'] = 'Este necesară cheia API.';
+        $this->data['auth_username_error'] = 'Nume utilizator este obligatoriu.';
+        $this->data['auth_password_error'] = 'Parola este necesară.';
+        $this->data['auth_fiscalcode_error'] = 'Codul fiscal este necesar.';
+        $this->data['success_auth'] = 'Datele de autentificare sunt corecte și acum aveți acces la sincronizare!';
+
+        //tab 3
+        $this->data['tab_name_3'] = 'Opțiuni comenzi';
+        $this->data['tab_name_3_1'] = 'Opțiuni de sincronizare a comenzii';
+        $this->data['tab_name_3_2'] = 'Opțiuni de sincronizare stoc';
+
+        $this->data['sync_orders_as'] = 'Sincronizați comenzile ca';
+        $this->data['proforma_series'] = 'Serie Proformă';
+        $this->data['days_ago'] = 'Sincronizați numai comenzile create în ultimele zile';
+        $this->data['auto_order_sync'] = 'Sincronizare automată a comenzilor';
+        $this->data['auto_stock_sync'] = 'Sincronizare automată stoc';
+        $this->data['activ'] = 'Activat';
+        $this->data['inactiv'] = 'Dezactivat';
+
+        $this->data['cron_job'] = 'Notă: Sincronizarea automată necesită un cron job cu următoarea adresă URL:';
+        $this->data['filter_stock'] = 'Filtrați stocul după punct de lucru și gestiune';
+        $this->data['filter_stock_all'] = 'Toate punctele de lucru';
+        $this->data['loading_data_please_wait'] = 'Se încărcă datele ... Vă rugăm să așteptați ...';
+
+        $this->data['option_proformaserie_error'] = 'Este necesară seria Proformă';   
+        $this->data['option_daysago_error'] = 'Câmpul este obligatoriu și trebuie să conțină doar cifre';  
+        $this->data['success_options'] = 'Ați salvat cu succes opțiunile selectate!';
+        $this->data['success_save_data'] = 'Ați salvat cu succes datele introduse!';
+        $this->data['locations'] = 'Alegeți locațiile în care se va actualiza stocul';
+
+         //tab 4
+         $this->data['tab_name_4'] = 'Optiuni stoc';
+         
+         $this->data['success_save_options'] = 'Ați salvat cu succes opțiunile introduse!';
+        $this->data['success_save_auth'] = 'Ați salvat cu succes datele de autentificare introduse!';
+
+		$this->data['with_discount'] = 'Includere discount în prețul final';
+        $this->data['with_discount_desc'] = '(valabil pentru comenzi, facturi si proforme)';
+
+        //tab 5
+        $this->data['tab_name_5'] = 'Jurnale de activitate';
+        $this->data['datepicker'] = 'ro';
+        $this->data['download_btn'] = 'Descarcă';
+        $this->data['clear_btn'] = 'Golește';
+        $this->data['tab_description_5_1'] = 'Selectați intervalul de timp și apoi apăsați butonul Descărcare pentru a obține jurnalele de activitate';
+        $this->data['tab_description_5_2'] = 'Apăsați Golește pentru a reseta toate jurnalele de activitate';
+        $this->data['success_download_log'] = 'Fișierul de jurnal a fost descărcat cu succes!';
+        $this->data['error_download_log'] = 'A apărut o eroare la descărcarea fișierului';
+        $this->data['download_log_error_1'] = 'Trebuie să completați data de început';
+        $this->data['download_log_error_2'] = 'Data de început trebuie să fie mai mică sau egal decât data de sfârșit';
+        $this->data['confirm_clear_log'] = 'Sunteți sigur că doriți să ștergeți fișierul jurnal ?';
+        $this->data['success_clear_log'] = 'Fișierul jurnal a fost șters cu succes!';
+        $this->data['error_clear_log'] = 'A apărut o eroare la ștergerea fișierului jurnal';
+        $this->data['log_header_date'] = 'Data';
+        $this->data['log_header_type'] = 'Tip';
+        $this->data['log_header_description'] = 'Descriere';
+        
+		$this->data['success_test_auth'] = 'Testarea pentru autentificare a fost finalizată cu succes';
+        $this->data['start_sync_process'] = 'A inceput procesul de sincronizare';
+
+        
+    }
+
+}

+ 0 - 0
Core/Language/index.php


+ 8 - 0
Core/Log/AnyLog.php

@@ -0,0 +1,8 @@
+<?php 
+
+namespace Core\Log;
+
+abstract class AnyLog 
+{    
+    abstract public static function write($message, $type);
+}

+ 35 - 0
Core/Log/FileLog.php

@@ -0,0 +1,35 @@
+<?php 
+
+namespace Core\Log;
+
+class FileLog extends AnyLog 
+{
+    const DATA_SEPARATOR = ' | ';
+    const ERROR = 'ERROR';
+    const INFO = 'INFO';
+    const DATA = 'DATA';
+
+    public static function write($message, $type)
+    {
+        $lineArray[] = date('Y-m-d H:i:s');
+        $lineArray[] = $type;
+        $lineArray[] = trim($message);
+        $lineArray[] = "\n";
+        
+        $file = 'logs/file-' . ((isset($_GET['shop'])) ? str_replace(SHOPIFY_DOMAIN, '', $_GET['shop']): '') . '.log';
+
+        // if (!file_exists($file)) {
+        //     touch($file);
+        // }
+
+        file_put_contents($file, implode(self::DATA_SEPARATOR, $lineArray), FILE_APPEND);
+    }
+
+    public static function getFile()
+    {
+        $file = 'logs/file-' . ((isset($_GET['shop'])) ? str_replace(SHOPIFY_DOMAIN, '', $_GET['shop']): '') . '.log';
+
+        return $file;
+    }
+
+}

+ 273 - 0
Database.php

@@ -0,0 +1,273 @@
+<?php 
+
+class Database
+{
+    public $conn;
+
+    const DB_TABLE = 'fact_conf_marketplace';
+
+    public function __construct()
+    {
+
+        $this->conn = @mysqli_connect(HOST, USERNAME, PASSWORD, DATABASE);
+
+        if (!$this->conn) {
+            echo 'Database connection failed: ' . mysqli_connect_error();
+            exit;
+        }
+
+    }
+
+    public function getConnection()
+    {
+        return $this->conn;
+    }
+
+    public function getAll()
+    {
+        $shops = array();
+        $sql = "SELECT * FROM " . self::DB_TABLE. " WHERE market_code LIKE '%" . SHOPIFY_DOMAIN . "%'";
+        
+        $result = mysqli_query($this->conn, $sql);
+
+        if (mysqli_num_rows($result) > 0) {
+            while($row = mysqli_fetch_assoc($result)) {
+                $shops[] = $row;
+            }
+        } 
+            
+        return $shops;
+    }
+
+    public function delete($shop)
+    {
+        $shopName = mysqli_real_escape_string($this->conn, $shop);
+        $sqls = "SELECT * FROM " . self::DB_TABLE. " WHERE market_code = '" . $shopName . "' AND market_code LIKE '%" . SHOPIFY_DOMAIN . "%'";
+        
+        $result = mysqli_query($this->conn, $sqls);
+
+        if (mysqli_num_rows($result) == 1) {
+            $sqlu = "UPDATE " . self::DB_TABLE. " SET market_apikey = '', market_conf_json = '' WHERE market_code = '" . $shopName . "' AND market_code LIKE '%" . SHOPIFY_DOMAIN . "%'";
+
+            if (!mysqli_query($this->conn, $sqlu)) {
+                throw new \Exception("Error deleting shop: " . mysqli_error($this->conn));
+            } 
+            else
+            {
+                echo "Shop deleted successfully";
+            }
+        }        
+    }
+
+    protected function merge($currentShopData, $newShopData)
+    {
+        if(empty($currentShopData))
+        {
+            return $newShopData;
+        }
+        else
+        {
+            return array_merge($currentShopData, $newShopData);
+        }
+        
+    }
+
+    protected function update($data)
+    {
+        if(isset($_REQUEST['shop']) && ($_REQUEST['shop'] != ''))
+        {
+            $shop = mysqli_real_escape_string($this->conn, $_REQUEST['shop']);
+
+            $sql = "UPDATE " . self::DB_TABLE. " SET market_conf_json = '" . json_encode($data) . "' WHERE market_code = '" .  $shop . "' AND market_code LIKE '%" . SHOPIFY_DOMAIN . "%'";
+
+            if (!mysqli_query($this->conn, $sql)) {
+                throw new \Exception($this->printException($sql . ": " . mysqli_error($this->conn)));
+            }
+        }
+        else
+        {
+            throw new \Exception($this->printException(SHOP_REQUIRED));
+        }
+    }
+
+    private function printException($message)
+    {
+        return 'Err Facturis Db: "' . $message . '". ';
+    }
+
+    public function getSettings($shop = '')
+    {
+        $shopName = '';
+        if(!empty($shop))
+        {
+            $shopName = mysqli_real_escape_string($this->conn, $shop);
+        }
+        else
+        {
+            if(isset($_REQUEST['shop']) && ($_REQUEST['shop'] != ''))
+            {
+                $shopName = mysqli_real_escape_string($this->conn, $_REQUEST['shop']); 
+            }
+        }
+
+        if(empty($shopName))
+        {
+            return array();
+        }
+       
+        $sql = "SELECT market_conf_json FROM " . self::DB_TABLE. " WHERE market_code = '" . $shopName . "' AND market_code LIKE '%" . SHOPIFY_DOMAIN . "%'";
+        $result = mysqli_query($this->conn, $sql);
+        if($result)
+        {
+            if (mysqli_num_rows($result)> 0) {
+                while($row = mysqli_fetch_assoc($result)) {
+                    return json_decode($row['market_conf_json'], true);
+                }
+            } else {
+                return array();
+            }
+        }
+        else
+        {
+            throw new \Exception($this->printException($sql . ": " . mysqli_error($this->conn)));
+        }
+        
+        return array();
+        
+    }
+
+    public function saveOptions()
+    {
+        $new = array();
+
+        if(isset($_POST['fsync_auth_apikey'])) $new['fsync_auth_apikey'] = $_POST['fsync_auth_apikey'];
+        if(isset($_POST['fsync_auth_username'])) $new['fsync_auth_username'] = $_POST['fsync_auth_username'];
+        if(isset($_POST['fsync_auth_password'])) $new['fsync_auth_password'] = $_POST['fsync_auth_password'];
+        if(isset($_POST['fsync_auth_fiscalcode'])) $new['fsync_auth_fiscalcode'] = $_POST['fsync_auth_fiscalcode'];
+
+        if(isset($_POST['fsync_option_autosyncstock'])) $new['fsync_option_autosyncstock'] = $_POST['fsync_option_autosyncstock'];
+        if(isset($_POST['fsync_option_autosyncorder'])) $new['fsync_option_autosyncorder'] = $_POST['fsync_option_autosyncorder'];
+        if(isset($_POST['fsync_option_filterstock'])) $new['fsync_option_filterstock'] = $_POST['fsync_option_filterstock'];
+        if(isset($_POST['fsync_option_syncordersas'])) $new['fsync_option_syncordersas'] = $_POST['fsync_option_syncordersas'];
+        if(isset($_POST['fsync_option_proformaserie'])) $new['fsync_option_proformaserie'] = $_POST['fsync_option_proformaserie'];
+        if(isset($_POST['fsync_option_daysago'])) $new['fsync_option_daysago'] = $_POST['fsync_option_daysago'];
+        if(isset($_POST['fsync_option_autosyncstock']) && !isset($_POST['fsync_option_daysago'])) $new['fsync_option_locations'] = '';
+        if(isset($_POST['fsync_option_locations'])) $new['fsync_option_locations'] = implode(", ", $_POST['fsync_option_locations']);
+        if(isset($_POST['fsync_option_withdiscount'])) $new['fsync_option_withdiscount'] = $_POST['fsync_option_withdiscount'];
+
+        $current = $this->getSettings();
+
+        $merged = $this->merge($current, $new);
+        $this->update($merged);  
+    }
+
+    public function getToken($shop = '')
+    {
+        $shopName = '';
+        if(!empty($shop))
+        {
+            $shopName = mysqli_real_escape_string($this->conn, $shop);
+        }
+        else
+        {
+            if(isset($_REQUEST['shop']) && ($_REQUEST['shop'] != ''))
+            {
+                $shopName = mysqli_real_escape_string($this->conn, $_REQUEST['shop']); 
+            }
+        }
+
+        if(empty($shopName))
+        {
+            return '';
+        }
+
+        $sql = "SELECT market_apikey FROM " . self::DB_TABLE. " WHERE market_code = '" . $shopName . "' AND market_code LIKE '%" . SHOPIFY_DOMAIN . "%'";
+        $result = mysqli_query($this->conn, $sql);
+        if($result)
+        {
+            if (mysqli_num_rows($result) > 0) {
+                while($row = mysqli_fetch_assoc($result)) {
+                    return $row['market_apikey'];
+                }
+            } else {
+                throw new \Exception($this->printException('A token is required for the script to run'));
+            }
+        }
+        else
+        {
+            throw new \Exception($this->printException("Error: " . $sql . ": " . mysqli_error($this->conn)));
+        }
+        
+        return '';
+               
+    }
+
+    public function generateToken()
+    {
+        $query = array(
+            "client_id" => API_KEY, 
+            "client_secret" => SHARED_SECRET, 
+            "code" => isset($_GET['code']) ?  $_GET['code'] : ''
+        );
+        
+        $access_token_url = "https://" . (isset($_REQUEST['shop']) ? $_REQUEST['shop'] : '') . "/admin/oauth/access_token";
+        
+        $ch = curl_init();
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
+        curl_setopt($ch, CURLOPT_URL, $access_token_url);
+        curl_setopt($ch, CURLOPT_POST, count($query));
+        curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($query));
+        $result = curl_exec($ch);
+        $curl_error = curl_error($ch);
+        
+        curl_close($ch);
+        
+        if($curl_error)
+        {
+            throw new \Exception('Error installation: ' . json_encode($curl_error));
+        }
+        
+        $result = json_decode($result, true);
+        if(!isset($result['access_token']))
+        {
+            throw new \Exception('Error installation: No token found');
+        }
+
+        return $result['access_token'];
+    }
+
+    public function add($access_token)
+    {
+        if(isset($_REQUEST['shop']) && ($_REQUEST['shop'] != ''))
+        {
+            $shop = mysqli_real_escape_string($this->conn, $_REQUEST['shop']);
+            $token = mysqli_real_escape_string($this->conn, $access_token);
+            
+            $sql = "SELECT * FROM " . self::DB_TABLE. " WHERE market_code = '" . $shop . "' AND market_code LIKE '%" . SHOPIFY_DOMAIN . "%'";
+            
+            $result = mysqli_query($this->conn, $sql);
+            if($result)
+            {
+                    if (mysqli_num_rows($result) > 0) 
+                    {
+                        $sql = "UPDATE " . self::DB_TABLE. " SET market_apikey = '" . $token . "', market_conf_json = '' WHERE market_code = '" . $shop . "' AND market_code LIKE '%" . SHOPIFY_DOMAIN . "%'"; 
+                    }
+                    else
+                    {
+                        $sql = 'INSERT INTO ' . self::DB_TABLE. ' (market_apikey, market_code) VALUE ("' . $token . '", "' . $shop . '")';
+                    }
+
+                    if (!mysqli_query($this->conn, $sql)) {
+                        throw new \Exception($this->printException($sql . ": " . mysqli_error($this->conn)));
+                    }
+            }
+            else
+            {
+                    throw new \Exception($this->printException($sql . ": " . mysqli_error($this->conn)));
+            }
+        }
+       
+       
+    }
+
+}

+ 229 - 0
FacturisSync.php

@@ -0,0 +1,229 @@
+<?php 
+
+class FacturisSync
+{
+    public $language;
+    public $db;
+
+    public function __construct($db) {
+        $this->language = $this->getLanguage();
+        $this->db = $db;
+    }
+    
+    private function  getLanguage()
+    { 
+        $language = new Core\Language\English();
+
+        return $language;
+    } 
+
+    public function showMenu()
+    {
+        $result = $this->downloadLog();
+
+        if(empty($result))
+        {
+            $result = $this->clearLog();
+        }
+
+        if(empty($result))
+        {
+        $result = $this->saveOptions();
+    	}
+
+        if(empty($result))
+        {
+            $result = $this->sync();
+        }
+        
+        $this->getForm($this->db, $result); 
+    }
+
+    function testAuth(){  
+        try{
+            $formController = new \Controller\FormController();
+            $formController->testAuth($this->db);
+			\Core\Log\FileLog::write($this->language->get('success_test_auth'), \Core\Log\FileLog::INFO);
+            echo '';exit;
+        }
+        catch(\Exception $e) {
+			\Core\Log\FileLog::write($e->getMessage(), \Core\Log\FileLog::ERROR);
+            echo $e->getMessage();exit;
+        }   
+    }
+
+    function getPdlGestiuni()
+    {
+        try{
+            $formController = new \Controller\FormController();
+            $result = $formController->getPdlGestiuni($this->db, $this->language);
+
+            echo $result;exit;
+        }
+        catch(\Exception $e) {
+			\Core\Log\FileLog::write($e->getMessage(), \Core\Log\FileLog::ERROR);
+            echo '';exit;
+        } 
+    }
+
+    function checkLatestVersion()
+    {
+        try{
+            $formController = new \Controller\FormController();
+            $result = $formController->checkLatestVersion($this->db, $this->language);
+
+            echo $result;exit;
+        }
+        catch(\Exception $e) {
+            \Core\Log\FileLog::write($e->getMessage(), \Core\Log\FileLog::ERROR);
+            echo '';exit;
+        } 
+    }
+
+    public function sync()
+    {   
+        try{
+            $syncController = new \Controller\SyncController($this->db, $this->language);
+            $result = $syncController->sync();
+            if(!empty($result))
+            {
+                \Core\Log\FileLog::write($result, \Core\Log\FileLog::INFO); 
+            }
+			
+            return array('success' => $result);  
+        }
+        catch(\Exception $e) {
+            $message = $this->language->get($e->getMessage());
+			\Core\Log\FileLog::write($message, \Core\Log\FileLog::ERROR);
+            return array('error' => $message);
+        } 
+    }
+
+    private function getForm($db, $result)
+    {
+        try{
+            extract($result);
+            $formController = new \Controller\FormController();
+            $data = $formController->getForm($db, $this->language);
+            extract($data);
+       
+            include('View/main.php');  
+        }
+        catch(\Exception $e) {
+            $message = $e->getMessage();
+			\Core\Log\FileLog::write($message, \Core\Log\FileLog::ERROR);
+            echo $message;
+        } 
+    }
+
+    function autoSync() {
+
+        try{
+            $shops = $this->db->getAll();
+            if(!empty($shops))
+            {
+                foreach($shops as $shop)
+                {
+                    if(isset($shop['market_code']))
+                    {
+                        try{
+                            $controller = new \Controller\SyncController($this->db, $this->language, $shop['market_code']);
+                            $result = $controller->autoSync();
+                            $message = 'Shop OK: ' . $shop['market_code'];
+                            echo $message . PHP_EOL;
+                            \Core\Log\FileLog::write($message, \Core\Log\FileLog::INFO);
+                        }
+                        catch(\Exception $e) {
+                        	$messsage = 'Err shop: ' . $shop['market_code'] . ': ' . $this->language->get($e->getMessage());
+                        	\Core\Log\FileLog::write($messsage, \Core\Log\FileLog::ERROR);
+                            echo $messsage . PHP_EOL;
+                        }
+                    }
+                }
+            }
+        }
+        catch(\Exception $e) {
+			$messsage = 'Err cron: ' . $this->language->get($e->getMessage());
+        	\Core\Log\FileLog::write($messsage, \Core\Log\FileLog::ERROR);
+            echo  $messsage . PHP_EOL;
+        } 
+    }
+	
+	public function downloadLog()
+    {
+        try{
+            if(isset($_REQUEST['action']) && ($_REQUEST['action'] == 'downloadLog'))
+            {
+                $header = array(
+                    $this->language->get('log_header_date'),
+                    $this->language->get('log_header_type'),
+                    $this->language->get('log_header_description') 
+                );
+                
+                $logController = new \Controller\LogController();
+                $logController->downloadFileLog(
+                    isset($_REQUEST['start']) ? $_REQUEST['start'] : date('d.m.Y H:i:s'),
+                    isset($_REQUEST['end']) ? $_REQUEST['end'] : date('d.m.Y H:i:s'),
+                    $header
+                );
+
+                return array('success' => $this->language->get('success_download_log'));  
+            }
+            else
+            {
+                return array();
+            }
+        }
+        catch(\Exception $e) {
+            $message = $this->language->get('error_download_log');
+            \Core\Log\FileLog::write($message, \Core\Log\FileLog::ERROR);
+            return array('error' => $message);
+        } 
+    }
+
+    public function clearLog()
+    {
+        try{
+            if(isset($_REQUEST['action']) && ($_REQUEST['action'] == 'clearLog'))
+            {
+                $result = null;
+                $logController = new \Controller\LogController();
+                $logController->clearFileLog();    
+
+                return array('success' => $this->language->get('success_clear_log'));  
+            }
+            else
+            {
+                return array();
+            }
+        }
+        catch(\Exception $e) {
+            $message = $this->language->get('error_clear_log');
+            \Core\Log\FileLog::write($message, \Core\Log\FileLog::ERROR);
+            return array('error' => $message);
+        } 
+    }
+
+    public function saveOptions()
+    {   
+        try{
+            if(isset($_REQUEST['action']) && $_REQUEST['action'] == 'save')
+            {
+                $this->db->saveOptions();
+                $message = $this->language->get('success_save_data');
+                \Core\Log\FileLog::write($message, \Core\Log\FileLog::INFO);
+                return array('success' => $message);  
+            }
+            else
+            {
+                return array();
+            }
+        }
+        catch(\Exception $e) {
+            $message = $this->language->get($e->getMessage());
+            \Core\Log\FileLog::write($message, \Core\Log\FileLog::ERROR);
+            return array('error' => $message);
+        } 
+    }
+  
+}

+ 297 - 0
MarketplaceRepository/OrderRepository.php

@@ -0,0 +1,297 @@
+<?php 
+
+namespace MarketplaceRepository;
+
+class OrderRepository 
+extends Repository
+implements \Core\App\Repository\Marketplace\OrderInterface
+{
+    public $remoteCustomers;
+    public $remoteProducts;
+    public $orderedDaysAgo;
+	public $withDiscount;
+    public $localProducts;
+
+    public function getAll()
+    {
+        $results = array();
+        $orderFactory = new \Core\App\Factory\OrderFactory();
+        $orderItemFactory = new \Core\App\Factory\OrderItemFactory();
+        $orderItemFactory->setPrefix(self::PREFIX);
+        $orderItemFactory->setRemoteProducts($this->remoteProducts);
+		$orderItemFactory->setLocalProducts($this->localProducts);
+        $orderItemFactory->setWithDiscountOption($this->withDiscount);
+
+        $orders = $this->getOrders();
+        if(!empty($orders))
+        {
+            foreach($orders as $order)
+            {
+                if($this->isValidOrder($order))
+                {
+                    $orderObj = $this->getOrder($orderFactory, $order);
+
+                    $orderProducts = $this->getOrderProducts($orderItemFactory, $order);
+                    $orderProducts = (array) $orderProducts;
+
+                    $orderTaxes = $this->getOrderTaxes($orderItemFactory, $order);
+                    $orderTaxes = (array) $orderTaxes;
+
+                    $result = array();
+                    $result['idMarketpalce'] = self::MARKETPLACE_ID;
+                    $result['dataFact'] = (array) $orderObj;
+                    $result['dataProd'] = array_merge($orderProducts, $orderTaxes);	
+
+                    $results[] = $result;   
+                }
+                
+            }
+        }
+
+        return $results;
+    }
+
+    public function setRemoteCustomers($remoteCustomers)
+    {
+        $this->remoteCustomers = $remoteCustomers;
+    }
+
+    public function setRemoteProducts($remoteProducts)
+    {
+        $this->remoteProducts = $remoteProducts;
+    }
+
+    public function getOrderProducts($orderItemFactory, $order)
+    {
+        $orderProducts = array();
+
+        $taxIncluded = $this->getTaxIncluded($order);
+
+        if(!empty($order['line_items']))
+        {
+            foreach($order['line_items'] as $line_item)
+            {
+                $taxRate = $this->getTaxRate($line_item);
+                $variant_id = $this->getVariantId($line_item['variant_id']);
+                $orderProductObj = $orderItemFactory->createOrderProduct(
+                    $line_item['id'] . $variant_id,
+                    $line_item['name'],
+                    '',//ean
+                    $line_item['sku'],
+            
+                    $order['currency'],
+                    $line_item['price'],
+                    $taxRate,
+                    $line_item['quantity'],
+                    0,
+                    $taxIncluded
+                );
+                $orderProducts = array_merge($orderProducts, $orderProductObj);
+            }
+        }
+
+        return $orderProducts;
+    }
+
+    public function getOrderTaxes($orderItemFactory, $order)
+    {
+        $orderTaxes = array();
+
+        $taxRate = (isset($order['tax_lines'][0]['rate']) ? $order['tax_lines'][0]['rate'] : 0);
+
+        if(
+            (isset($order['total_discounts_set']['shop_money']['amount'])) &&
+            (!empty((int)$order['total_discounts_set']['shop_money']['amount']) || (self::SHOW_EMPTY_TAX))
+        )
+            {
+            $orderTaxObj = $orderItemFactory->createOrderTax(
+                'Discount',
+                $order['currency'],
+                (-1) * $order['total_discounts_set']['shop_money']['amount'],
+                (-1) * ($taxRate * $order['total_discounts_set']['shop_money']['amount'])
+            );  
+
+            $orderTaxes[] = (array) $orderTaxObj;
+        }
+
+        if(
+            (isset($order['total_shipping_price_set']['shop_money']['amount'])) &&
+            (!empty((int)$order['total_shipping_price_set']['shop_money']['amount']) || (self::SHOW_EMPTY_TAX))
+        )
+        {
+
+            $orderTaxObj = $orderItemFactory->createOrderTax(
+                'Taxa de livrare',
+                $order['currency'],
+                $order['total_shipping_price_set']['shop_money']['amount'],
+                $taxRate * $order['total_shipping_price_set']['shop_money']['amount']
+            );  
+            
+            $orderTaxes[] = (array) $orderTaxObj;
+        }
+
+        return $orderTaxes;
+    }
+
+    protected function getTaxIncluded($order)
+    {
+        if($order['taxes_included'] == 1)
+        {
+            return true;
+        }
+        else
+        {
+            return false;
+        }
+    }
+
+    public function getCodF($billing_address_company)
+    {
+
+        $billing_address_company = $this->stripSpecialChars($billing_address_company);
+ 
+        if(
+            !empty($billing_address_company) && 
+            in_array(trim($billing_address_company), array_column($this->remoteCustomers, 'Denumire'))
+        )
+        {
+            $index = array_search($billing_address_company, array_column($this->remoteCustomers, 'Denumire'));
+            return $this->remoteCustomers[$index]['CUI_CNP'];
+        }
+
+        return '';
+    }
+    
+    protected function getPaymentMethod($order)
+    {
+        if(isset($order['payment_gateway_names']) && !empty($order['payment_gateway_names']))
+        {
+            return implode(', ', $order['payment_gateway_names']);
+        }
+        else 
+        {
+            if(isset($order['gateway']))
+            {
+                return $order['gateway'];	
+            }
+        }
+
+        return '';
+    }
+
+	public function setWithDiscount($withDiscount)
+    {
+        $this->withDiscount = $withDiscount;
+    }
+	
+	public function setLocalProducts($localProducts)
+    {
+        if(!empty($localProducts))
+        {
+            foreach($localProducts as $localProduct)
+            {
+                $this->localProducts[] = (array) $localProduct;
+            }
+        }
+        else
+        {
+            $this->localProducts = array();
+        }
+    }
+
+    protected function getTaxRate($line_item)
+    {
+        $taxRate = 0;
+        if(isset($line_item['tax_lines']) && !empty($line_item['tax_lines']) && ($line_item['taxable'] == 1))
+        {
+            foreach($line_item['tax_lines'] as $tax_line)
+            {
+                if(isset($tax_line['rate']))
+                {
+                    $taxRate = $tax_line['rate'];
+                }
+            }
+        }
+
+        return $taxRate;
+    }
+
+    public function getOrder($orderFactory, $order)
+    {
+        $orderObj = $orderFactory->createOrder(
+            isset($order['order_number']) ? $order['order_number']: '', 
+            isset($order['created_at']) ? $order['created_at'] : '', 
+            isset($order['currency']) ? $order['currency'] : '',
+            isset($order['customer']['id']) ? $order['customer']['id']: '',
+            isset($order['customer']['first_name']) ? $order['customer']['first_name'] : '',
+            isset($order['customer']['last_name']) ? $order['customer']['last_name'] : '',
+            isset($order['billing_address']['company']) ? $order['billing_address']['company'] : '',
+            isset($order['email']) ? $order['email'] : '',
+
+            isset($order['billing_address']['first_name']) ? $order['billing_address']['first_name'] : '',
+            isset($order['billing_address']['last_name']) ? $order['billing_address']['last_name'] : '',
+            isset($order['billing_address']['address1']) ? $order['billing_address']['address1'] : '',
+            isset($order['billing_address']['address2']) ? $order['billing_address']['address2'] : '',
+            isset($order['billing_address']['zip']) ? $order['billing_address']['zip'] : '',
+            isset($order['billing_address']['city']) ? $order['billing_address']['city'] : '',
+            isset($order['billing_address']['province']) ? $order['billing_address']['province'] : '',
+            isset($order['billing_address']['phone']) ? $order['billing_address']['phone'] : '',
+            
+            isset($order['shipping_address']['first_name']) ? $order['shipping_address']['first_name'] : '',
+            isset($order['shipping_address']['last_name']) ? $order['shipping_address']['last_name'] : '',
+            isset($order['shipping_address']['address1']) ? $order['shipping_address']['address1'] : '',
+            isset($order['shipping_address']['address2']) ? $order['shipping_address']['address2'] : '',
+            isset($order['shipping_address']['zip']) ? $order['shipping_address']['zip'] : '',
+            isset($order['shipping_address']['city']) ? $order['shipping_address']['city'] : '',
+            isset($order['shipping_address']['province']) ? $order['shipping_address']['province'] : '',
+            isset($order['shipping_address']['phone']) ? $order['shipping_address']['phone'] : '',
+
+            isset($order['financial_status']) ? $order['financial_status'] : '',
+            $this->getPaymentMethod($order),
+            '',//comentarii
+            $this->getCodF((isset($order['billing_address']['company']) ? $order['billing_address']['company'] : '')),//cod fiscal 
+            '',//denumire firma - camp custom
+            ''//nr de inreg
+
+        );
+
+        return $orderObj;
+
+    }
+
+    public function isValidOrder($order)
+    {
+        $date1 = date('Y-m-d', strtotime('-' . (int) $this->orderedDaysAgo . ' days'));
+        $date2 = date('Y-m-d', strtotime($order['created_at']));
+
+        $dateTimestamp1 = strtotime($date1); 
+        $dateTimestamp2 = strtotime($date2); 
+
+        if ($dateTimestamp1 <= $dateTimestamp2) 
+            return true;
+        else
+            return false;
+    }
+
+    public function setOrderedDaysAgo($orderedDaysAgo)
+    {
+        $this->orderedDaysAgo = $orderedDaysAgo;
+    }
+
+    public function getVariantId($variant_id)
+    {
+        if(!empty($variant_id))
+        {
+            return '_' . $variant_id;
+        }
+        else
+        {
+            return '';
+        }
+    }
+
+	public function getByOrderId($orderId)
+    {
+        return null;
+    }
+}

+ 68 - 0
MarketplaceRepository/ProductRepository.php

@@ -0,0 +1,68 @@
+<?php 
+
+namespace MarketplaceRepository;
+
+class ProductRepository 
+extends Repository
+implements \Core\App\Repository\Marketplace\ProductInterface
+{
+    public function getAll($withOutDiscount = false)
+    {
+        $productFactory = new \Core\App\Factory\ProductFactory();
+        $productFactory->setPrefix(self::PREFIX);
+
+        $results = array();
+    
+        $taxRate = $this->getTax();
+        $currency = $this->getCurrency();
+        $taxIncluded = $this->areTaxesIncluded();
+    
+        $products = $this->getProducts();
+        if(!empty($products))
+        {
+            foreach($products as $product)
+            {
+                if(isset($product['variants']) && !empty($product['variants']))
+                {	
+                    foreach($product['variants'] as $variant)
+                    {   
+                        $finalPrice = $variant['price'];
+                        if($withOutDiscount && ($variant['compare_at_price'] > $variant['price']))
+                        {
+                            $finalPrice = $variant['compare_at_price'];
+                        }
+
+                        $productObj = $productFactory->createProduct(
+                            $product['id'] . '_' . $variant['id'],
+                            $product['title'] . ' - ' . $variant['title'],
+                            '',
+                            $variant['sku'],
+                            $currency,
+                            $finalPrice,
+                            ($variant['taxable'] == 1) ? $taxRate : 0,
+                            $this->getProdTip($product['published_at']),
+                            $taxIncluded
+                        );
+
+                        $results[] = $productObj;
+                    }
+                }
+            }
+        }
+
+        return $results;
+    }
+
+    private function getProdTip($published_at)
+    {
+        if(!empty($published_at))
+        {
+            return 'produs';
+        }
+        else 
+        {
+            return 'inactiv';
+        }
+    }
+
+}

+ 118 - 0
MarketplaceRepository/ProformaRepository.php

@@ -0,0 +1,118 @@
+<?php 
+
+namespace MarketplaceRepository;
+
+class ProformaRepository 
+extends OrderRepository
+implements \Core\App\Repository\Marketplace\ProformaInterface
+{
+    public $proformaSerie;
+
+    public function getAll()
+    {
+        $proformas = array();
+        $proformaFactory = new \Core\App\Factory\ProformaFactory();
+        $proformaFactory->setPrefix(self::PREFIX);
+        $proformaItemFactory = new \Core\App\Factory\ProformaItemFactory();
+        $proformaItemFactory->setPrefix(self::PREFIX);
+        $proformaItemFactory->setRemoteProducts($this->remoteProducts);
+        $proformaItemFactory->setLocalProducts($this->localProducts);
+        $proformaItemFactory->setWithDiscountOption($this->withDiscount);
+
+        $orders = $this->getOrders();
+        if(!empty($orders))
+        {
+            foreach($orders as $order)
+            {
+                if($this->isValidOrder($order))
+                {
+                    $proformaObj = $this->getProforma($proformaFactory, $order);
+
+                    $proformaProducts = $this->getOrderProducts($proformaItemFactory, $order);
+                    $proformaProducts = (array) $proformaProducts;
+
+                    $proformaTaxes = $this->getOrderTaxes($proformaItemFactory, $order);
+                    $proformaTaxes = (array) $proformaTaxes;
+
+                    $result = array();
+                    $result['idMarketpalce'] = self::MARKETPLACE_ID;
+                    $result['dataFact'] = (array) $proformaObj;
+                    $result['dataProd'] = array_merge($proformaProducts, $proformaTaxes);	
+                    $proformas[] = $result;
+                }   
+            }
+        }
+        
+        return $proformas;
+
+    }
+
+    public function getProforma($proformaFactory, $proforma)
+    {
+        $proformaObj = $proformaFactory->createProforma(
+            $this->proformaSerie, 
+            isset($proforma['order_number']) ? $proforma['order_number'] : '', 
+            isset($proforma['created_at']) ? $proforma['created_at'] : '',  
+            isset($proforma['currency']) ? $proforma['currency'] : '', 
+
+            isset($proforma['customer']['id']) ? $proforma['customer']['id']: '',
+            isset($proforma['customer']['first_name']) ? $proforma['customer']['first_name'] : '', 
+            isset($proforma['customer']['last_name']) ? $proforma['customer']['last_name'] : '', 
+            isset($proforma['billing_address']['company']) ? $proforma['billing_address']['company'] : '', 
+            isset($proforma['email']) ? $proforma['email'] : '', 
+
+            isset($proforma['billing_address']['first_name']) ? $proforma['billing_address']['first_name'] : '', 
+            isset($proforma['billing_address']['last_name']) ? $proforma['billing_address']['last_name'] : '', 
+            isset($proforma['billing_address']['address1']) ? $proforma['billing_address']['address1'] : '', 
+            isset($proforma['billing_address']['address2']) ? $proforma['billing_address']['address2'] : '', 
+            isset($proforma['billing_address']['zip']) ? $proforma['billing_address']['zip'] : '', 
+            isset($proforma['billing_address']['city']) ? $proforma['billing_address']['city'] : '', 
+            isset($proforma['billing_address']['province']) ? $proforma['billing_address']['province'] : '', 
+            isset($proforma['billing_address']['phone']) ? $proforma['billing_address']['phone'] : '', 
+        
+            isset($proforma['shipping_address']['first_name']) ? $proforma['shipping_address']['first_name'] : '', 
+            isset($proforma['shipping_address']['last_name']) ? $proforma['shipping_address']['last_name'] : '', 
+            isset($proforma['shipping_address']['address1']) ? $proforma['shipping_address']['address1'] : '', 
+            isset($proforma['shipping_address']['address2']) ? $proforma['shipping_address']['address2'] : '', 
+            isset($proforma['shipping_address']['zip']) ? $proforma['shipping_address']['zip'] : '', 
+            isset($proforma['shipping_address']['city']) ? $proforma['shipping_address']['city'] : '', 
+            isset($proforma['shipping_address']['province']) ? $proforma['shipping_address']['province'] : '', 
+            isset($proforma['shipping_address']['phone']) ? $proforma['shipping_address']['phone'] : '', 
+
+            $this->getPaymentMethod($proforma),
+            '',//comentarii
+            $this->getCodF((isset($proforma['billing_address']['company']) ? $proforma['billing_address']['company'] : '')),
+            '',//companie 
+            '',//$nr_inreg,
+            '',//$facturi_cont_client,
+            '',//$facturi_banca_client
+            $this->getProformaTaxRate($proforma)
+        );
+
+        return $proformaObj;
+
+    }
+
+    public function setProformaSerie($proformaSerie)
+    {
+        $this->proformaSerie = $proformaSerie;
+    }
+
+    protected function getProformaTaxRate($proforma)
+    {
+        if(!isset($proforma['line_items']) || empty($proforma['line_items']))
+        {
+            return 0;
+        }
+
+        foreach($proforma['line_items'] as $line_item)
+        {
+            if(isset($line_item['tax_lines'][0]['rate']))
+            {
+                return $line_item['tax_lines'][0]['rate'];
+            }
+        }
+
+        return 0;
+    }
+}

+ 246 - 0
MarketplaceRepository/Repository.php

@@ -0,0 +1,246 @@
+<?php 
+
+namespace MarketplaceRepository;
+
+class Repository
+{
+    
+    const TAX_COUNTRY = 'Romania';
+    const MARKETPLACE_ID = 6;
+    const PREFIX = 'SHOPIFY_';
+    const SHOW_EMPTY_TAX = false;
+    public $token;
+    public $shop;
+
+    public function __construct($token, $shop)
+    {
+        $this->token = $token;
+        $this->shop = $shop;
+    }
+
+    private function shopifyCall($token, $shop, $api_endpoint, $query = array(), $method = 'GET', $request_headers = array()) {
+    
+        $url = "https://" . $shop . ".myshopify.com" . $api_endpoint;
+        
+		if (!is_null($query) && in_array($method, array('GET', 	'DELETE'))) $url = $url . "?" . http_build_query($query);
+
+		$curl = curl_init($url);
+		curl_setopt($curl, CURLOPT_HEADER, TRUE);
+		curl_setopt($curl, CURLOPT_RETURNTRANSFER, TRUE);
+		curl_setopt($curl, CURLOPT_FOLLOWLOCATION, TRUE);
+		curl_setopt($curl, CURLOPT_MAXREDIRS, 3);
+		curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
+
+		curl_setopt($curl, CURLOPT_USERAGENT, 'My New Shopify App v.1');
+		curl_setopt($curl, CURLOPT_CONNECTTIMEOUT, 100);
+		curl_setopt($curl, CURLOPT_TIMEOUT, 600);
+		curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
+
+		$request_headers[] = "";
+		if (!is_null($token)) $request_headers[] = "X-Shopify-Access-Token: " . $token;
+		curl_setopt($curl, CURLOPT_HTTPHEADER, $request_headers);
+
+		if ($method != 'GET' && in_array($method, array('POST', 'PUT'))) {
+			if (is_array($query)) $query = http_build_query($query);
+			curl_setopt ($curl, CURLOPT_POSTFIELDS, $query);
+		}
+		
+		$response = curl_exec($curl);
+		$error_number = curl_errno($curl);
+		$error_message = curl_error($curl);
+		curl_close($curl);
+
+		sleep(3);
+
+		if ($error_number) 
+		{
+            if(is_array($error_message))
+			{
+                throw new \Exception($this->printException(json_encode($error_message) . $api_endpoint));
+			}
+			else
+			{
+                throw new \Exception($this->printException($error_message . $api_endpoint));
+            }
+        }
+
+		$response = preg_split("/\r\n\r\n|\n\n|\r\r/", $response, 2);
+        if(isset($response[1]))
+        {
+            $response[1] = json_decode($response[1], true);
+            if(isset($response[1]['errors']))
+            {
+                if(is_array($response[1]['errors']))
+                {
+                    throw new \Exception($this->printException(json_encode($response[1]['errors']) . $api_endpoint));
+                }
+                else
+                {
+                    throw new \Exception($this->printException($response[1]['errors'] . $api_endpoint));
+                }
+            }
+            else
+            {
+                return $response[1];
+            }
+        }
+        else
+        {
+            throw new \Exception($this->printException('err CURL'));
+        }
+    }
+    
+    public function getCurrency()
+	{
+        $result = $this->shopifyCall($this->token, $this->shop, "/admin/shop.json", array(), 'GET');
+        if(!isset($result['shop']['currency']))
+        {
+            throw new \Exception($this->printException('no currency information found'));
+        }
+
+        return $result['shop']['currency'];
+    }
+    
+    public function getTax()
+	{
+        $result = $this->shopifyCall($this->token, $this->shop, "/admin/countries.json", array(), 'GET');
+        if(!isset($result['countries']) || empty($result['countries']))
+        {
+            throw new \Exception($this->printException('no information found on VAT value (error code: 1)'));
+        }
+
+        foreach($result['countries'] as $country)
+        {
+            if(isset($country['name']) && $country['name'] == self::TAX_COUNTRY)
+            {
+                if(isset($country['tax']))
+                {
+                    return $country['tax'];
+                }   
+                
+            }
+        }
+        
+        throw new \Exception($this->printException('no information found on VAT value (error code: 2)'));
+    }
+    
+    public function areTaxesIncluded()
+	{
+        $result = $this->shopifyCall($this->token, $this->shop, "/admin/shop.json", array(), 'GET');
+        if(!isset($result['shop']['taxes_included']))
+        {
+            throw new \Exception($this->printException('no information found on VAT value (error code: 3)'));
+        }
+            
+        return $result['shop']['taxes_included'];
+    }
+
+    public function getProducts()
+	{
+		$result = $this->shopifyCall($this->token, $this->shop, "/admin/products.json", array(), 'GET');
+        
+        if(!isset($result['products']))
+        {
+            throw new \Exception($this->printException('no product information found'));
+        }
+        
+        return $result['products'];
+    }
+    
+    public function getOrders()
+	{
+		$result = $this->shopifyCall($this->token, $this->shop, "/admin/orders.json?status=any", array(), 'GET');
+        if(!isset($result['orders']))
+        {
+            throw new \Exception($this->printException('no order information found'));
+        }
+        
+        return array_reverse($result['orders']) ;
+	}
+
+    public function getLocations()
+	{
+		$result = $this->shopifyCall($this->token, $this->shop, "/admin/locations.json", array(), 'GET');
+        if(!isset($result['locations']))
+        {
+            throw new \Exception($this->printException('no location information found'));
+        }
+        
+        return $result['locations'];
+	}
+
+	public function getProductVariant($productId, $variantId)
+	{
+		$result = $this->shopifyCall($this->token, $this->shop, "/admin/products/" . $productId . "/variants/" . $variantId . ".json", array(), 'GET');
+
+        if(!isset($result['variant']))
+        {
+            throw new \Exception($this->printException('no information was found about the product option with the id: ' . $variantId . ' which belongs to the product with the id: ' . $productId));
+        }
+		
+		return $result['variant'];
+	}
+
+	public function getInventoryLevels($inventory_item_id)
+	{
+		$result = $this->shopifyCall($this->token, $this->shop, "/admin/inventory_levels.json?inventory_item_ids=" . $inventory_item_id, array(), 'GET');
+        if(!isset($result['inventory_levels']))
+        {
+            throw new \Exception($this->printException('no inventory level information found for inventory item id: ' . $inventory_item_id));
+        }
+        
+        return $result['inventory_levels'];
+	}
+
+	public function setInventoryLevel($location_id, $inventory_item_id, $qty, $disconnect_if_necessary)
+	{
+		$post = array(
+			'location_id' => $location_id,
+			'inventory_item_id' => $inventory_item_id,
+			'available' => (int) $qty,
+			'disconnect_if_necessary' => $disconnect_if_necessary
+		);
+
+		$this->shopifyCall($this->token, $this->shop, "/admin/inventory_levels/set.json", $post, 'POST');
+	}
+
+	public function adjustInventoryLevel($location_id, $inventory_item_id, $adjustQty, $oldQty)
+	{
+		if((int)$adjustQty != 0)
+		{
+			$post = array(
+				'location_id' => $location_id,
+				'inventory_item_id' => $inventory_item_id,
+				'available_adjustment' => (int) $adjustQty,
+			);
+	
+			$result = $this->shopifyCall($this->token, $this->shop, "/admin/inventory_levels/adjust.json", $post, 'POST');
+
+			$oldQty = (int) $oldQty;
+			$adjustQty = (int) $adjustQty;
+
+			if(
+				!isset($result['inventory_level']['available']) || 
+				($oldQty + $adjustQty) !== (int) $result['inventory_level']['available']
+			)
+			{
+				throw new \Exception($this->printException('the inventory level with the inventory item id: ' . $inventory_item_id . ' and location id: ' . $location_id . ' was not adjusted correctly'));
+			}
+		}
+    }
+
+    protected function stripSpecialChars($string)
+	{
+		$string = str_replace(array("\n", "\r", "&nbsp;"), array('', '', ''), $string);
+		$string = preg_replace('/[%&\'"]/', '', $string);
+		$string = html_entity_decode($string, ENT_COMPAT, 'UTF-8');
+        $string = preg_replace( '/[^[:print:]]/', '',$string);
+
+        return $string;
+    }
+
+    protected function printException($message)
+    {
+        return 'Err Shopify: "' . $message . '". ';
+    }
+}

+ 258 - 0
MarketplaceRepository/SettingsRepository.php

@@ -0,0 +1,258 @@
+<?php 
+
+namespace MarketplaceRepository;
+
+class SettingsRepository 
+implements \Core\App\Repository\Marketplace\SettingsInterface
+{
+    const VERSION_PLATFORM_CODE = 'SHOPIFY';
+    const VERSION_FILE = 'https://facturis-online.ro/plugins/latest_versions.txt';
+    const VERSION_SEPARATOR = ", ";
+
+    public $settings;
+    public $shop;
+    public $db;
+
+    public function __construct($db, $shop = '')
+    {
+        $this->conn = $db->getConnection();
+        $this->db = $db;
+        
+        $this->setShop($shop);
+
+        $settings = $db->getSettings($this->shop);
+       
+        $settingsFactory = new \Core\App\Factory\SettingsFactory();
+        $this->settings = $settingsFactory->createSettings(
+            isset($settings['fsync_auth_apikey']) ? $settings['fsync_auth_apikey'] : '',
+            isset($settings['fsync_auth_username']) ? $settings['fsync_auth_username'] : '',
+            isset($settings['fsync_auth_password']) ? $settings['fsync_auth_password'] : '',
+            isset($settings['fsync_auth_fiscalcode']) ? $settings['fsync_auth_fiscalcode'] : '',
+
+            isset($settings['fsync_option_autosyncstock']) ? $settings['fsync_option_autosyncstock'] : '',
+            isset($settings['fsync_option_autosyncorder']) ? $settings['fsync_option_autosyncorder'] : '',
+            isset($settings['fsync_option_filterstock']) ? $settings['fsync_option_filterstock'] : '',
+            isset($settings['fsync_option_syncordersas']) ? $settings['fsync_option_syncordersas'] : '',
+            isset($settings['fsync_option_proformaserie']) ? $settings['fsync_option_proformaserie'] : '',
+            isset($settings['fsync_option_daysago']) ? $settings['fsync_option_daysago'] : '',
+            isset($settings['fsync_option_locations']) ? $settings['fsync_option_locations'] : null,
+			isset($settings['fsync_option_withdiscount']) ? $settings['fsync_option_withdiscount'] : ''
+        );
+    }
+
+    public function getAuthApiKey()
+    {
+        return $this->settings->getAuthApiKey();
+    }
+
+    public function getAuthUsername()
+    {
+        return $this->settings->getAuthUsername();
+    }
+
+    public function getAuthPassword()
+    {
+        return $this->settings->getAuthPassword();
+    }
+
+    public function getAuthFiscalCode()
+    {
+        return $this->settings->getAuthFiscalCode();
+    }
+
+    public function getOptionAutoStockSync()
+    {
+        return $this->settings->getOptionAutoStockSync();
+    }
+
+    public function getOptionAutoOrderSync()
+    {
+        return $this->settings->getOptionAutoOrderSync();
+    }
+
+    public function getOptionStockFilter()
+    {
+        return $this->settings->getOptionStockFilter();
+    }
+
+    public function getOptionOrderOrProforma()
+    {
+        return $this->settings->getOptionOrderOrProforma();
+    }
+
+    public function getOptionProformaSerie()
+    {
+        return $this->settings->getOptionProformaSerie();
+    }
+
+    public function getOptionOrderedDaysAgo()
+    {
+        return $this->settings->getOptionOrderedDaysAgo();
+    }
+
+    public function getOptionLocations()
+    {
+        return $this->settings->getOptionLocations();
+    }
+
+    public function getCustomFieldBank()
+    {
+        return $this->settings->getCustomFieldBank();
+    }
+
+    public function getCustomFieldAccount()
+    {
+        return $this->settings->getCustomFieldAccount();
+    }
+
+    public function getCustomFieldCompany()
+    {
+        return $this->settings->getCustomFieldCompany();
+    }
+
+    public function getCustomFieldFiscalCode()
+    {
+        return $this->settings->getCustomFieldFiscalCode();
+    }
+
+    public function getCustomFieldRegNumber()
+    {
+        return $this->settings->getCustomFieldRegNumber();
+    }
+
+    public function getSyncDataTypes()
+    {
+        return $this->settings->getSyncDataTypes();
+    }
+
+    public function getSyncOptions()
+    {
+        return array(
+            'fsync_option_autosyncstock' => $this->settings->getOptionAutoStockSync(),
+            'fsync_option_autosyncorder' => $this->settings->getOptionAutoOrderSync(),
+            'fsync_option_filterstock' => $this->settings->getOptionStockFilter(),
+            'fsync_option_syncordersas' =>  $this->settings->getOptionOrderOrProforma(),
+            'fsync_option_proformaserie' => $this->settings->getOptionProformaSerie(),
+            'fsync_option_daysago' => $this->settings->getOptionOrderedDaysAgo(),
+            'fsync_option_locations' => $this->settings->getOptionLocations(),
+        	'fsync_option_withdiscount' => $this->settings->getOptionWithDiscount()
+        );
+
+    }
+
+    public function getSyncAuth()
+    {
+        return array(
+            'fsync_auth_apikey' => $this->settings->getAuthApiKey(),
+            'fsync_auth_username' =>  $this->settings->getAuthUsername(),
+            'fsync_auth_password' => $this->settings->getAuthPassword(),
+            'fsync_auth_fiscalcode' => $this->settings->getAuthFiscalCode()
+        );
+
+    }
+
+    public function getToken()
+    {
+        return $this->db->getToken($this->shop);
+    }
+
+    public function getShop()
+    {
+        if(!empty($this->shop))
+        {
+            return str_replace(SHOPIFY_DOMAIN, '', $this->shop);
+        }
+        else
+        {
+            throw new \Exception('A shop name is required for the script to run');
+        }
+    }
+    public function getVersionLink()
+    {
+        return VERSION_LINK;
+    }
+    
+    public function getVersion()
+    {
+        return VERSION;
+    }
+
+    public function setShop($shop = '')
+    {
+        $this->shop = '';
+        if(!empty($shop))
+        {
+            $this->shop = $shop;
+        }
+        else
+        {
+            if(isset($_GET['shop']))
+            {
+                $this->shop = $_GET['shop'];
+            }
+        }
+    }
+
+    public function getSyncOrdersAs()
+    {
+        return array(
+            \Core\App\Factory\SettingsFactory::SELECT_ORDER,
+            \Core\App\Factory\SettingsFactory::SELECT_PROFORMA 
+        );
+    }
+
+    public function getAutoSyncOptions()
+    {
+        return array(
+            \Core\App\Factory\SettingsFactory::AUTO_SYNC_ENABLED,
+            \Core\App\Factory\SettingsFactory::AUTO_SYNC_DISABLED 
+        );
+    }
+
+    public function getOptionWithDiscount()
+    {
+        return $this->settings->getOptionWithDiscount();
+    }
+
+    public function getEnabledDisabledOptions()
+    {
+        return array(
+            \Core\App\Factory\SettingsFactory::SYNC_ENABLED,
+            \Core\App\Factory\SettingsFactory::SYNC_DISABLED 
+        );
+    }
+
+    public function checkLatestVersion($language)
+    {
+        $errorMessage = '<h4><span class="alert danger alert-box">' . $language->get('version_check_error') . '</span></h4>';
+        $versionFile = @fopen(self::VERSION_FILE, "r") or die($errorMessage);
+
+        while(!feof($versionFile)) {
+            $versionRow = explode(self::VERSION_SEPARATOR, fgets($versionFile));
+            if($versionRow[0] == self::VERSION_PLATFORM_CODE && isset($versionRow[1]))
+            {
+                if(trim($this->getVersion()) == trim($versionRow[1]))
+                {
+                    $successMessage = '<small style="color: #74B807 !important">' . $language->get('version_check_success') . '</small>';
+                }
+                else
+                { 
+                    $successMessage = '<small style="color: red !important">' . $language->get('version_check_new') . ': <b>' . $versionRow[1] . '</b>';
+                    if(isset($versionRow[2]) && !empty($versionRow[2]))
+                    {
+                        $successMessage .= ' ' . $language->get('version_check_link_1') . '</small> <u><a style="color: red !important" target="_blank" href="' . $versionRow[2] . '">';
+                        $successMessage .=  $language->get('version_check_link_2');
+                        $successMessage .= '</a></u>';
+                    }
+                }
+
+                echo $successMessage;
+                exit;
+            }
+        }
+        echo '';
+        fclose($versionFile);
+    }
+
+
+}

+ 101 - 0
MarketplaceRepository/StockRepository.php

@@ -0,0 +1,101 @@
+<?php 
+
+namespace MarketplaceRepository;
+
+
+class StockRepository 
+extends Repository
+implements \Core\App\Repository\Marketplace\StockInterface
+{
+    public $stockLocations;
+	public $localProducts;
+
+	public function setLocalProducts($localProducts)
+    {
+        if(!empty($localProducts))
+        {
+            foreach($localProducts as $localProduct)
+            {
+                $this->localProducts[] = (array) $localProduct;
+            }
+        }
+    }
+	
+	public function update($remoteStock)
+    {
+    	\Core\Log\FileLog::write('Facturis Stock: ' . json_encode($remoteStock), \Core\Log\FileLog::DATA);   
+        \Core\Log\FileLog::write('Shop Products: ' . json_encode($this->localProducts), \Core\Log\FileLog::DATA);
+
+        $this->updateStock($remoteStock);
+    }
+
+    public function getShopLocations()
+    {
+        $shopLocations = array();
+        $locations = $this->getLocations();
+
+        if(!empty($locations))
+        {
+            foreach($locations as $location)
+            {
+                $shopLocations[] = array('id' => $location['id'], 'name' => $location['name']);
+            }
+        }
+        
+        return $shopLocations;
+    }
+
+    public function setStockLocations($locations)
+    {
+        $this->stockLocations = $locations;
+    }
+
+	//update
+    private function updateStock($remoteStock)
+    {
+        if(!empty($remoteStock))
+        {
+            foreach($remoteStock as $remoteStockItem)
+            {
+                if (in_array($remoteStockItem['product'], array_column($this->localProducts, $remoteStockItem['type']))) {
+                    $index = array_search($remoteStockItem['product'], array_column($this->localProducts, $remoteStockItem['type']));
+
+                    $product_ids = explode('_', $this->localProducts[$index]['prod_cod1']);
+
+                    $variant_ids = $product_ids;
+                    unset($variant_ids[0]);
+                    unset($variant_ids[1]);
+                    if(!empty($variant_ids))
+                    {
+                        foreach($variant_ids as $variant_id)
+                        {
+                            $this->updateQty($remoteStockItem['qty'], $product_ids[1], $variant_id);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private function updateQty($qty, $product_id, $variant_id)
+    {
+        $productVariant = $this->getProductVariant($product_id, $variant_id);
+        if(!empty($productVariant))
+        {
+            $resultInventoryLevels = $this->getInventoryLevels($productVariant['inventory_item_id']);
+            if(!empty($resultInventoryLevels))
+            {
+                foreach($resultInventoryLevels as $inventoryLevel)
+                {
+                    $adjustQty = ((-1) * $inventoryLevel['available']) + (int) $qty;
+                    
+                    if((is_array($this->stockLocations) && in_array($inventoryLevel['location_id'], $this->stockLocations)) || 
+                    ($this->stockLocations == \Core\App\Factory\SettingsFactory::DEFAULT_LOCATIONS))
+                    {
+                        $this->adjustInventoryLevel($inventoryLevel['location_id'], $inventoryLevel['inventory_item_id'], $adjustQty, $inventoryLevel['available']); 
+                    }
+                }
+            }
+        }
+    }
+}

+ 365 - 0
View/css/form.css

@@ -0,0 +1,365 @@
+.facturissynctab__content * {
+    -webkit-box-sizing: border-box !important;
+    -moz-box-sizing: border-box !important;
+    box-sizing: border-box !important;
+}
+  
+.facturissynctab__content  {
+    padding: 20px 15% !important;
+}
+
+.facturissynctab__content  form
+{
+    font-family: sans-serif !important;
+    color: #3c3c3c !important;
+}
+  
+.facturissynctab__content  form header {
+    margin: 0 0 20px 0 !important; 
+}
+
+.facturissynctab__content form header div {
+    font-size: 85% !important;
+    color: #999 !important;
+}
+
+.facturissynctab__content form header h2 {
+    margin: 0 0 5px 0 !important;
+}
+
+.facturissynctab__content form > div {
+    clear: both !important;
+    overflow: hidden !important;
+    padding: 4px !important;
+    margin: 0 0 10px 0 !important;
+}
+
+.facturissynctab__content form > div > fieldset > div > div {
+    margin: 0 0 5px 0 !important;
+}
+
+.facturissynctab__content form > div > label,
+.facturissynctab__content legend {
+    width: 25% !important;
+    float: left !important;
+    padding-right: 10px !important;
+    font-size: 14px !important;
+    position: relative !important;
+    top: 5px !important;
+}
+
+.facturissynctab__content form#order_option_form > div > label,
+.facturissynctab__content form#order_option_form legend,
+.facturissynctab__content form#stock_option_form > div > label,
+.facturissynctab__content form#stock_option_form legend{
+    top: 0px !important;
+}
+
+.facturissynctab__content form > div > div,
+.facturissynctab__content form > div > fieldset > div {
+    width: 75% !important;
+    float: right !important;
+}
+
+.facturissynctab__content form > div > fieldset label {
+    font-size: 90% !important;
+}
+
+.facturissynctab__content fieldset {
+    border: 0 !important;
+    padding: 0 !important;
+}
+
+.facturissynctab__content input[type=text],
+.facturissynctab__content input[type=email],
+.facturissynctab__content input[type=url],
+.facturissynctab__content input[type=password],
+.facturissynctab__content select,
+.facturissynctab__content textarea {
+    width: 300px !important;
+    border-width: 1px !important;
+    border-style: solid !important;
+    border-color: #c1c1c1 !important;
+    padding: 7px !important;
+    /*padding: 4px !important; */
+    border-radius: 5px !important;
+}
+
+.facturissynctab__content input[type=text]:focus,
+.facturissynctab__content input[type=email]:focus,
+.facturissynctab__content input[type=url]:focus,
+.facturissynctab__content input[type=password]:focus,
+.facturissynctab__content textarea:focus {
+    outline: 0 !important;
+    border-color: #4697e4 !important;
+}
+
+.facturissynctab__content input[type=radio], 
+.facturissynctab__content input[type=radio]+label{
+    cursor: pointer !important;
+}
+
+
+@media (max-width: 600px) {
+    .facturissynctab__content form > div {
+        margin: 0 0 15px 0 !important; 
+    }
+
+    .facturissynctab__content form > div > label,
+    .facturissynctab__content legend {
+        width: 100% !important;
+        /*float: none !important;*/
+        margin: 0 0 5px 0 !important;
+    }
+
+    .facturissynctab__content form > div > div,
+    .facturissynctab__content form > div > fieldset > div {
+        width: 100% !important;
+        float: none !important;
+    }
+
+    .facturissynctab__content input[type=text],
+    .facturissynctab__content input[type=email],
+    .facturissynctab__content input[type=url],
+    .facturissynctab__content input[type=password],
+    .facturissynctab__content textarea,
+    .facturissynctab__content select {
+        width: 100% !important; 
+    }
+}
+@media (min-width: 1200px) {
+    .facturissynctab__content form > div > label,
+    .facturissynctab__content  legend {
+        text-align: right !important;
+    }
+}
+
+.facturissynctab__content .greenButton {
+    box-shadow:inset 0px 1px 0px 0px #a4e271 !important;
+    background:linear-gradient(to bottom, #89c403 5%, #77a809 100%) !important;
+    background-color:#89c403 !important;
+    border-radius:6px !important;
+    border:1px solid #74b807 !important;
+    display:inline-block !important;
+    cursor:pointer !important;
+    color:#ffffff !important;
+    font-family:Arial !important;
+    font-size:15px !important;
+    font-weight:bold !important;
+    padding:9px 24px !important;
+    text-decoration:none !important;
+    text-shadow:0px 1px 0px #528009 !important;
+}
+
+.facturissynctab__content .greenButton:hover {
+    background:linear-gradient(to bottom, #77a809 5%, #89c403 100%) !important;
+    background-color:#77a809 !important;
+}
+
+.facturissynctab__content .greenButton:active {
+    position:relative !important;
+    top:1px !important;
+}
+
+.facturissynctab__content .blueButton {
+    box-shadow:inset 0px 1px 0px 0px #97c4fe !important;
+    background:linear-gradient(to bottom, #3d94f6 5%, #1e62d0 100%) !important;
+    background-color:#3d94f6 !important;
+    border-radius:6px !important;
+    border:1px solid #337fed !important;
+    display:inline-block !important;
+    cursor:pointer !important;
+    color:#ffffff !important;
+    font-family:Arial !important;
+    font-size:15px !important;
+    /*font-weight:bold !important;*/
+    padding:9px 24px !important;
+    text-decoration:none !important;
+    /*text-shadow:0px 1px 0px #1570cd !important;*/
+}
+
+.facturissynctab__content .blueButton:hover {
+    background:linear-gradient(to bottom, #1e62d0 5%, #3d94f6 100%) !important;
+    background-color:#1e62d0 !important;
+}
+
+.facturissynctab__content .blueButton:active {
+    position:relative !important;
+    top:1px !important;
+}
+
+.facturissynctab__content .center
+{
+    text-align: center !important;
+}
+
+.facturissynctab__content .red
+{
+    color: red !important;
+    padding: 6px !important;
+}
+
+.facturissynctab__content .hide
+{
+    display: none;
+}
+
+.btn {
+    display: inline-block;
+    padding: 6px 12px !important;
+    margin-bottom: 0 !important;
+    font-size: 14px !important;
+    font-weight: normal !important;
+    line-height: 1.428571429 !important;
+    text-align: center !important;
+    white-space: nowrap !important;
+    vertical-align: middle !important;
+    cursor: pointer !important;
+    background-image: none !important;
+    border: 1px solid transparent !important;
+    border-radius: 4px !important;
+    -webkit-user-select: none !important;
+    -moz-user-select: none !important;
+    -ms-user-select: none !important;
+    -o-user-select: none !important;
+    user-select: none !important;
+    text-decoration:none !important;
+}
+
+.btn-blue { 
+    color: #ffffff !important; 
+    background-color: #337FED !important; 
+    border-color: #1570cd !important; 
+} 
+
+.btn-blue:hover, 
+.btn-blue:focus, 
+.btn-blue:active, 
+.btn-blue.active, 
+.open .dropdown-toggle.btn-blue { 
+    color: #ffffff !important; 
+    background-color: #1570CD !important; 
+    border-color: #1570cd !important; 
+} 
+
+.btn-blue:active, 
+.btn-blue.active, 
+.open .dropdown-toggle.btn-blue { 
+    background-image: none !important; 
+} 
+
+.btn-blue.disabled, 
+.btn-blue[disabled], 
+fieldset[disabled] .btn-blue, 
+.btn-blue.disabled:hover, 
+.btn-blue[disabled]:hover, 
+fieldset[disabled] .btn-blue:hover, 
+.btn-blue.disabled:focus, 
+.btn-blue[disabled]:focus, 
+fieldset[disabled] .btn-blue:focus, 
+.btn-blue.disabled:active, 
+.btn-blue[disabled]:active, 
+fieldset[disabled] .btn-blue:active, 
+.btn-blue.disabled.active, 
+.btn-blue[disabled].active, 
+fieldset[disabled] .btn-blue.active { 
+    background-color: #337FED !important; 
+    border-color: #1570cd !important; 
+} 
+
+.btn-blue .badge { 
+    color: #337FED !important; 
+    background-color: #ffffff !important; 
+}
+
+.btn-green { 
+    color: #ffffff !important; 
+    background-color: #74B807 !important; 
+    border-color: #528009 !important; 
+} 
+
+.btn-green:hover, 
+.btn-green:focus, 
+.btn-green:active, 
+.btn-green.active, 
+.open .dropdown-toggle.btn-green { 
+    color: #ffffff !important; 
+    background-color: #528009 !important; 
+    border-color: #528009 !important; 
+} 
+
+.btn-green:active, 
+.btn-green.active, 
+.open .dropdown-toggle.btn-green { 
+    background-image: none !important; 
+} 
+
+.btn-green.disabled, 
+.btn-green[disabled], 
+fieldset[disabled] .btn-green, 
+.btn-green.disabled:hover, 
+.btn-green[disabled]:hover, 
+fieldset[disabled] .btn-green:hover, 
+.btn-green.disabled:focus, 
+.btn-green[disabled]:focus, 
+fieldset[disabled] .btn-green:focus, 
+.btn-green.disabled:active, 
+.btn-green[disabled]:active, 
+fieldset[disabled] .btn-green:active, 
+.btn-green.disabled.active, 
+.btn-green[disabled].active, 
+fieldset[disabled] .btn-green.active { 
+    background-color: #74B807 !important; 
+    border-color: #528009 !important; 
+} 
+
+.btn-green .badge { 
+    color: #74B807 !important; 
+    background-color: #ffffff !important; 
+}
+
+.btn-light-blue { 
+    color: #ffffff !important; 
+    background-color: #84BBF3 !important; 
+    border-color: #528ECC !important; 
+  } 
+   
+  .btn-light-blue:hover, 
+  .btn-light-blue:focus, 
+  .btn-light-blue:active, 
+  .btn-light-blue.active, 
+  .open .dropdown-toggle.btn-light-blue { 
+    color: #ffffff !important; 
+    background-color: #528ECC !important; 
+    border-color: #528ECC !important; 
+  } 
+   
+  .btn-light-blue:active, 
+  .btn-light-blue.active, 
+  .open .dropdown-toggle.btn-light-blue { 
+    background-image: none !important; 
+  } 
+   
+  .btn-light-blue.disabled, 
+  .btn-light-blue[disabled], 
+  fieldset[disabled] .btn-light-blue, 
+  .btn-light-blue.disabled:hover, 
+  .btn-light-blue[disabled]:hover, 
+  fieldset[disabled] .btn-light-blue:hover, 
+  .btn-light-blue.disabled:focus, 
+  .btn-light-blue[disabled]:focus, 
+  fieldset[disabled] .btn-light-blue:focus, 
+  .btn-light-blue.disabled:active, 
+  .btn-light-blue[disabled]:active, 
+  fieldset[disabled] .btn-light-blue:active, 
+  .btn-light-blue.disabled.active, 
+  .btn-light-blue[disabled].active, 
+  fieldset[disabled] .btn-light-blue.active { 
+    background-color: #84BBF3 !important; 
+    border-color: #528ECC !important; 
+  } 
+   
+  .btn-light-blue .badge { 
+    color: #84BBF3 !important; 
+    background-color: #ffffff !important; 
+  }

+ 305 - 0
View/css/main.css

@@ -0,0 +1,305 @@
+.facturissynctab-wrap {
+    border-radius: 6px !important;
+    max-width: 100% !important;
+    display: -webkit-box !important;
+    display: flex !important;
+    flex-wrap: wrap !important;
+    position: relative !important;
+    list-style: none !important;
+    background-color: #fff !important;
+    /*margin: 40px 0 !important;*/
+    margin: 10px 0 40px !important;
+    border-color: #c2c2c2 !important;
+    border-width: 1px !important;
+    border-style: solid !important;
+}
+
+.facturissynctab {
+    display: none !important;
+}
+
+.facturissynctab:checked:nth-of-type(1) ~ .facturissynctab__content:nth-of-type(1) {
+    opacity: 1 !important;
+    -webkit-transition: 0.5s opacity ease-in, 0.8s -webkit-transform ease !important;
+    transition: 0.5s opacity ease-in, 0.8s -webkit-transform ease !important;
+    transition: 0.5s opacity ease-in, 0.8s transform ease !important;
+    transition: 0.5s opacity ease-in, 0.8s transform ease, 0.8s -webkit-transform ease !important;
+    position: relative !important;
+    top: 0 !important;
+    z-index: 100 !important;
+    -webkit-transform: translateY(0px) !important;
+            transform: translateY(0px) !important;
+    text-shadow: 0 0 0 !important;
+}
+
+.facturissynctab:checked:nth-of-type(2) ~ .facturissynctab__content:nth-of-type(2) {
+    opacity: 1 !important;
+    -webkit-transition: 0.5s opacity ease-in, 0.8s -webkit-transform ease !important;
+    transition: 0.5s opacity ease-in, 0.8s -webkit-transform ease !important;
+    transition: 0.5s opacity ease-in, 0.8s transform ease !important;
+    transition: 0.5s opacity ease-in, 0.8s transform ease, 0.8s -webkit-transform ease !important;
+    position: relative !important;
+    top: 0 !important;
+    z-index: 100 !important;
+    -webkit-transform: translateY(0px) !important;
+            transform: translateY(0px) !important;
+    text-shadow: 0 0 0 !important;
+}
+
+.facturissynctab:checked:nth-of-type(3) ~ .facturissynctab__content:nth-of-type(3) {
+    opacity: 1 !important;
+    -webkit-transition: 0.5s opacity ease-in, 0.8s -webkit-transform ease !important;
+    transition: 0.5s opacity ease-in, 0.8s -webkit-transform ease !important;
+    transition: 0.5s opacity ease-in, 0.8s transform ease !important;
+    transition: 0.5s opacity ease-in, 0.8s transform ease, 0.8s -webkit-transform ease !important;
+    position: relative !important;
+    top: 0 !important;
+    z-index: 100 !important;
+    -webkit-transform: translateY(0px) !important;
+            transform: translateY(0px) !important;
+    text-shadow: 0 0 0 !important;
+}
+
+.facturissynctab:checked:nth-of-type(4) ~ .facturissynctab__content:nth-of-type(4) {
+    opacity: 1 !important;
+    -webkit-transition: 0.5s opacity ease-in, 0.8s -webkit-transform ease !important;
+    transition: 0.5s opacity ease-in, 0.8s -webkit-transform ease !important;
+    transition: 0.5s opacity ease-in, 0.8s transform ease !important;
+    transition: 0.5s opacity ease-in, 0.8s transform ease, 0.8s -webkit-transform ease !important;
+    position: relative !important;
+    top: 0 !important;
+    z-index: 100 !important;
+    -webkit-transform: translateY(0px) !important;
+            transform: translateY(0px) !important;
+    text-shadow: 0 0 0 !important;
+}
+
+.facturissynctab:checked:nth-of-type(5) ~ .facturissynctab__content:nth-of-type(5) {
+    opacity: 1 !important;
+    -webkit-transition: 0.5s opacity ease-in, 0.8s -webkit-transform ease !important;
+    transition: 0.5s opacity ease-in, 0.8s -webkit-transform ease !important;
+    transition: 0.5s opacity ease-in, 0.8s transform ease !important;
+    transition: 0.5s opacity ease-in, 0.8s transform ease, 0.8s -webkit-transform ease !important;
+    position: relative !important;
+    top: 0 !important;
+    z-index: 100 !important;
+    -webkit-transform: translateY(0px) !important;
+            transform: translateY(0px) !important;
+    text-shadow: 0 0 0 !important;
+}
+
+.facturissynctab:first-of-type:not(:last-of-type) + label {
+    border-top-right-radius: 0 !important;
+    border-bottom-right-radius: 0 !important;
+}
+
+.facturissynctab:not(:first-of-type):not(:last-of-type) + label {
+    border-radius: 0 !important;
+}
+
+.facturissynctab:last-of-type:not(:first-of-type) + label {
+    border-top-left-radius: 0 !important;
+    border-bottom-left-radius: 0 !important;
+}
+
+.facturissynctab:checked + label {
+    background-color: #528ecc !important; /* #fff !important; */
+    cursor: default !important;
+}
+
+.facturissynctab:checked + label:hover {
+    background-color: #528ecc !important; /* #fff !important; */
+}
+
+.facturissynctab + label {
+    border-radius: 6px 6px 0 0 !important;
+    cursor: pointer !important;
+    display: block !important;
+    text-decoration: none !important;
+    color: #fff !important; /* #333 !important; */
+    -webkit-box-flex: 3 !important;
+            flex-grow: 3 !important;
+    text-align: center !important;
+    background-color: #84bbf3 !important; /*#dbebff !important;*/ /* #e8e8e8 !important; */
+    -webkit-user-select: none !important;
+        -moz-user-select: none !important;
+        -ms-user-select: none !important;
+            user-select: none !important;
+    text-align: center !important;
+    height: 50px !important;
+    box-sizing: border-box !important;
+    padding: 15px !important;
+    font-size: 15px;
+}
+
+.facturissynctab + label:hover {
+    /*background-color: #bddbfa !important; */ /* #f9f9f9 !important; */
+    text-decoration: underline !important;
+}
+
+.facturissynctab__content {
+    padding: 10px 25px !important;
+    background-color: transparent !important;
+    position: absolute !important;
+    width: 100% !important;
+    z-index: -1 !important;
+    opacity: 0 !important;
+    left: 0 !important;
+    -webkit-transform: translateY(-3px) !important;
+            transform: translateY(-3px) !important;
+    border-radius: 6px !important;
+}
+
+
+.facturis-online-sync {
+    font-family: 'Helvetica', sans-serif !important;
+    background-color: #f6f6f6 !important;
+    color: #777 !important;
+    padding: 10px 0 !important;
+    font-weight: 300 !important;
+    margin-top: 10px;
+}
+
+.facturis-online-sync-container {
+    margin: 0 auto !important;
+    display: block !important;
+    max-width: 800px !important;
+}
+
+/*
+.facturis-online-sync-container > *:not(.facturissynctab-wrap) {
+    padding: 0 80px !important;
+}*/
+
+
+.facturis-online-sync h1,
+.facturis-online-sync h2 {
+    margin: 17px 0px !important;
+    color: #444 !important;
+    text-align: center !important;
+    font-weight: 400 !important;
+}
+
+.facturis-online-sync h2 {
+    font-size: 1em !important;
+    margin-bottom: 30px !important;
+}
+
+.facturis-online-sync h3 {
+    font-weight: 400 !important;
+}
+
+/*
+.facturis-online-sync p {
+    line-height: 1.6 !important;
+    margin-bottom: 20px !important;
+}*/
+
+.facturis-online-sync .alert,
+.facturis-online-sync .notice {
+    padding: 17px !important;
+    color: #721c24 !important;
+    background-color: #f8d7da !important;
+    /*opacity: 1 !important;
+    transition: opacity 0.6s !important;
+    margin-bottom: 15px !important;*/
+    border-radius: 7px;
+    margin: 0px !important;
+    border-width: 1px !important;
+    border-color: #cfcfcf  !important;
+    font-size: 13px;
+}
+  
+.facturis-online-sync .alert.success,
+.facturis-online-sync .notice-success {
+    background-color: #d4edda !important;
+    color: #155724 !important;
+}
+.facturis-online-sync .alert.info {
+    color: #004085 !important;
+    background-color: #cce5ff !important;
+    margin-bottom: 10px !important;
+}
+.facturis-online-sync .alert.warning {
+    color: #856404 !important;
+    background-color: #fff3cd !important;
+}
+  
+.facturis-online-sync .closebtn,
+.facturis-online-sync .notice-dismiss{
+    margin-left: 15px !important;
+    color: #b4b4b4 !important;
+    font-weight: bold !important;
+    float: right !important;
+    font-size: 22px !important;
+    line-height: 20px !important;
+    cursor: pointer !important;
+    transition: 0.3s !important;
+}
+
+.closebtn:hover {
+    color: black !important;
+}
+
+.facturis-online-sync p a{
+    color: #444 !important;
+    text-align: center !important;
+    font-weight: 400 !important;
+    font-size: 13px;
+    text-decoration: none;
+}
+
+.facturis-online-sync p a:hover {
+    text-decoration: underline;
+}
+
+.facturis-online-sync .spinner
+{
+    position: relative;
+    top: 2px;
+    left: 28px;
+}
+
+.facturis-online-sync .hide-element
+{
+    display: none;
+}
+
+#datepicker-container input
+{
+    width: 30% !important;
+}
+
+.datepicker
+{
+	font-family: 'Helvetica', sans-serif !important;
+}
+
+.datepicker-controls button.view-switch
+{
+    background-color: #528ecc !important;
+    color: white !important;
+}
+
+.datepicker-controls button.prev-btn,
+.datepicker-controls button.next-btn,
+.datepicker-cell.selected, .datepicker-cell.selected:hover,
+.datepicker-cell.range-end:not(.selected), .datepicker-cell.range-start:not(.selected)
+{
+    background-color: #84bbf3 !important;
+    color: white !important;
+}
+
+.datepicker-cell.today:not(.selected) {
+    background-color: #74B807 !important;
+}
+
+.datepicker-controls button.prev-btn
+{
+    margin-right: 2px !important;
+}
+
+.datepicker-controls button.next-btn
+{
+    margin-left: 2px !important;
+}

BIN
View/img/loader.gif


+ 0 - 0
View/index.php


+ 40 - 0
View/js/tab1.js

@@ -0,0 +1,40 @@
+function validateSyncForm()
+{
+    document.getElementById("fsync-datatype-error").style.display = "none";
+    document.getElementById("fsync-authform-error").style.display = "none";
+
+    if(document.getElementById("fsync_datatype").value == '')
+    {
+        document.getElementById("fsync-datatype-error").style.display = "block";
+        return false;
+    }
+
+    if(
+        document.getElementById("fsync_auth_apikey").value == '' ||
+        document.getElementById("fsync_auth_username").value == '' ||
+        document.getElementById("fsync_auth_password").value == '' ||
+        document.getElementById("fsync_auth_fiscalcode").value == '' 
+    )
+    {
+        document.getElementById("fsync-authform-error").style.display = "block";
+        return false;
+    }
+
+    return true;
+}
+
+
+function submitSyncForm()
+{
+    if(validateSyncForm()) 
+    { 
+        document.getElementById('btn_sync_form').disabled = true;
+
+        var alertboxes = document.getElementsByClassName("alert-box");
+	    for (var i = 0, length = alertboxes.length; i < length; i++) {
+            alertboxes[i].style.display = "none";
+        }
+        document.getElementById("alert-sync").style.display = "block";
+        document.getElementById('sync_form').submit();
+    }
+}

+ 103 - 0
View/js/tab2.js

@@ -0,0 +1,103 @@
+function validateAuth()
+{
+    $error = false;
+    if(document.getElementById("fsync_auth_apikey").value == '')
+    {
+        document.getElementById("fsync_auth_apikey_error").style.display = "block";
+        $error = true;
+    }
+    else
+    {
+        document.getElementById("fsync_auth_apikey_error").style.display = "none";
+    }
+
+    if(document.getElementById("fsync_auth_username").value == '')
+    {
+        document.getElementById("fsync_auth_username_error").style.display = "block";
+        $error = true;
+    }
+    else
+    {
+        document.getElementById("fsync_auth_username_error").style.display = "none";
+    }
+
+    if(document.getElementById("fsync_auth_password").value == '')
+    {
+        document.getElementById("fsync_auth_password_error").style.display = "block";
+        $error = true;
+    }
+    else
+    {
+        document.getElementById("fsync_auth_password_error").style.display = "none";
+    }
+
+    if(document.getElementById("fsync_auth_fiscalcode").value == '')
+    {
+        document.getElementById("fsync_auth_fiscalcode_error").style.display = "block";
+        $error = true;
+    }
+    else
+    {
+        document.getElementById("fsync_auth_fiscalcode_error").style.display = "none";
+    }
+
+    if($error)
+    {
+        return false;
+    }
+    else
+    {
+        testAuth();
+    }
+
+}
+
+function submitAuthForm()
+{
+    document.getElementById("authform_error").style.display = "none";
+    validateAuth();
+}
+
+function ascii_to_hexa(str)
+{
+    var arr1 = [];
+    for (var n = 0, l = str.length; n < l; n ++) 
+        {
+        var hex = Number(str.charCodeAt(n)).toString(16);
+        arr1.push(hex);
+        }
+    return arr1.join('');
+}
+
+function testAuth()
+{
+    var params = '';
+    params += '&fsync_auth_apikey=' + document.getElementById("fsync_auth_apikey").value;
+    params += '&fsync_auth_username=' + document.getElementById("fsync_auth_username").value;
+    params += '&fsync_auth_password=' + document.getElementById("fsync_auth_password").value;
+    params += '&fsync_auth_fiscalcode=' + document.getElementById("fsync_auth_fiscalcode").value;
+    params = encodeURI(params);
+
+    var xhttp;
+    xhttp = new XMLHttpRequest();
+    xhttp.onreadystatechange = function() {
+        if (this.readyState == 4 && this.status == 200) {
+            if( this.responseText == '')
+            {
+                document.getElementById("fsync_auth_password").value = ascii_to_hexa(document.getElementById("fsync_auth_password").value);
+		document.getElementById('save-auth-btn').classList.remove('btn-green');
+    		document.getElementById('save-auth-btn').innerHTML = '<img src="View/img/loader.gif" />';
+                document.getElementById('auth_form').submit();
+            }
+            else
+            {
+                document.getElementById("authform_error").style.display = "block";
+                document.getElementById("authform_error").innerHTML = this.responseText;
+            }
+            
+        }
+    };
+
+    xhttp.open("GET","ajax.php?random=" + Math.random() + "&action=testAuth" + params,true);
+    xhttp.send();  
+}

+ 71 - 0
View/js/tab3.js

@@ -0,0 +1,71 @@
+
+
+function validateOption()
+{
+    document.getElementById("fsync_option_proformaserie_error").style.display = "none";
+    document.getElementById("fsync_option_daysago_error").style.display = "none";
+
+    var days = document.getElementById("fsync_option_daysago").value;
+    $error = false;
+    if(days == '' || !days.match(/^\d+$/)) 
+    {
+        document.getElementById("fsync_option_daysago_error").style.display = "block";
+        $error = true;
+    }
+
+    var radios = document.getElementsByClassName("fsync_option_syncordersas");
+	for (var i = 0, length = radios.length; i < length; i++) {
+        if (radios[i].checked) {
+            if(radios[i].value == 'proforma' && document.getElementById("fsync_option_proformaserie").value == '') 
+            { 
+                document.getElementById("fsync_option_proformaserie_error").style.display = "block";
+                $error = true;
+            }
+        }
+    }  
+
+    if($error)
+    {
+        return false;
+    }
+    else
+    {
+        return true;
+    }
+    
+}
+
+function submitOrderOptionForm()
+{
+    if(validateOption()) { 
+	document.getElementById('save-oo-btn').classList.remove('btn-green');
+	document.getElementById('save-oo-btn').innerHTML = '<img src="View/img/loader.gif" />';
+        document.getElementById('order_option_form').submit();
+    }
+}
+
+
+
+function showOrHideProformaSerie()
+{
+    var radios = document.getElementsByClassName("fsync_option_syncordersas");
+	for (var i = 0, length = radios.length; i < length; i++) {
+        if (radios[i].checked) {
+            if(radios[i].value == 'proforma') 
+            { 
+                document.getElementById("fsync_option_proformaserie").parentElement.parentElement.style.display = 'block';
+            } 
+            else 
+            {  
+                document.getElementById("fsync_option_proformaserie").parentElement.parentElement.style.display = 'none';
+            }
+
+            break;
+        }
+    }    
+}
+
+
+
+
+

+ 30 - 0
View/js/tab4.js

@@ -0,0 +1,30 @@
+function getPdlGestiuni()
+{
+    var params = '';
+    params += '&fsync_option_filterstock=' + document.getElementById("fsync_option_filterstock_hidden").value;
+    params += '&shop=' + document.getElementById("fsync_shop_hidden").value;
+    params = encodeURI(params);
+
+    var xhttp;
+    xhttp = new XMLHttpRequest();
+    xhttp.onreadystatechange = function() {
+        if (this.readyState == 4 && this.status == 200) {
+            if( this.responseText != '')
+            {
+                document.getElementById("fsync_option_filterstock").innerHTML = this.responseText;
+            }
+        }
+    };
+
+    xhttp.open("GET","ajax.php?random=" + Math.random() + "&action=getPdlGestiuni" + params,true);
+    xhttp.send();  
+}
+
+function submitStockOptionForm()
+{
+    if(validateOption()) { 
+	document.getElementById('save-so-btn').classList.remove('btn-green');
+	document.getElementById('save-so-btn').innerHTML = '<img src="View/img/loader.gif" />';
+        document.getElementById('stock_option_form').submit();
+    }
+}

+ 54 - 0
View/js/tab5.js

@@ -0,0 +1,54 @@
+function validateDownloadForm()
+{
+    document.getElementById("fsync-download-1-error").style.display = "none";
+    document.getElementById("fsync-download-2-error").style.display = "none";
+
+    if(document.getElementById("input-date-start").value == '')
+    {
+        //document.getElementById('input-date-stop').value = new Date().toDateInputValue();
+        document.getElementById("fsync-download-1-error").style.display = "block";
+        return false;
+    }
+
+    if(document.getElementById("input-date-start").value > document.getElementById("input-date-stop").value)
+    {
+        //document.getElementById('input-date-stop').value = new Date().toDateInputValue();
+        document.getElementById("fsync-download-2-error").style.display = "block";
+        return false;
+    }
+
+    return true;
+}
+
+
+function submitDownloadForm()
+{
+    if(validateDownloadForm()) 
+    { 
+        document.getElementById('download_log_form').submit();
+    }
+}
+
+function clearLog()
+{
+	document.getElementById('clear_btn').classList.remove('btn-blue');
+	document.getElementById('clear_btn').innerHTML = '<img src="View/img/loader.gif" />';
+    	document.getElementById('clear_log_form').submit();
+}
+
+function checkLatestVersion()
+{
+    var xhttp;
+    xhttp = new XMLHttpRequest();
+    xhttp.onreadystatechange = function() {
+        if (this.readyState == 4 && this.status == 200) {
+            if( this.responseText != '')
+            {
+                document.getElementById("check_latest_version_result").innerHTML = this.responseText;
+            }
+        }
+    };
+
+    xhttp.open("GET", "ajax.php?random=" + Math.random() + "&action&action=checkLatestVersion",true);
+    xhttp.send();  
+}

+ 144 - 0
View/main.php

@@ -0,0 +1,144 @@
+<html>
+  <head>
+    <title><?php echo $title;?></title>
+    <link rel="stylesheet" href="View/css/main.css">
+    <link rel="stylesheet" href="View/css/form.css">
+    <link rel="stylesheet" href="View/vanillajs-datepicker/dist/css/datepicker.min.css">
+    <link rel="stylesheet" href="View/vanillajs-datepicker/dist/css/datepicker-bs4.min.css">
+  </head>
+  <body>
+
+    <div class="facturis-online-sync">
+      <h1><?php echo $title;?></h1>
+      <h2><?php echo $description; ?></h2>
+
+      <div class="facturis-online-sync-container">
+
+      <?php if($show_tutorial): ?>
+        <div class="alert info">
+        <span class="closebtn" onclick="this.parentElement.style.display = 'none';">&times;</span>  
+          <div style="text-align: center; font-size: 16px; margin-bottom: 22px;margin-top: 8px;"><?php echo $tutorial_title; ?></div>
+          <ol><div style="margin-bottom: 17px;"><?php echo $tutorial_p; ?>:</div>
+            <li><?php echo $tutorial_1; ?></li>
+            <li><?php echo $tutorial_2; ?></li>
+            <li><?php echo $tutorial_3; ?></li>
+            <li><?php echo $tutorial_4; ?></li>
+          </ol>
+        </div>
+      <?php endif; ?>
+
+      <div class="alert success hide-element" id="alert-sync">
+        <span class="closebtn" onclick="this.parentElement.style.display = 'none';">&times;</span>  
+        <?php echo $sync_please_wait; ?> <img class="spinner" src="View/img/loader.gif"/>
+      </div>
+    
+      <?php if(isset($success) && !empty($success)): ?>
+        <div class="alert success alert-box">
+          <span class="closebtn" onclick="this.parentElement.style.display = 'none';">&times;</span>  
+          <?php echo $success;?>
+        </div>
+      <?php endif; ?>
+
+      <?php if(isset($error) && !empty($error)): ?>
+        <div class="alert alert-box">
+          <span class="closebtn" onclick="this.parentElement.style.display = 'none';">&times;</span>  
+          <?php echo $error;?>
+        </div>
+      <?php endif; ?>
+
+        <!--
+        <div class="alert">
+          <span class="closebtn" onclick="this.parentElement.style.display = 'none';">&times;</span>  
+          <strong>Danger!</strong> Indicates a dangerous or potentially negative action.
+        </div>
+
+        <div class="alert success">
+          <span class="closebtn" onclick="this.parentElement.style.display = 'none';">&times;</span>  
+          <strong>Success!</strong> Indicates a successful or positive action.
+        </div>
+
+        <div class="alert info">
+          <span class="closebtn" onclick="this.parentElement.style.display = 'none';">&times;</span>  
+          <strong>Info!</strong> Indicates a neutral informative change or action.
+        </div>
+
+        <div class="alert warning">
+          <span class="closebtn" onclick="this.parentElement.style.display = 'none';">&times;</span>  
+          <strong>Warning!</strong> Indicates a warning that might need attention.
+        </div> -->
+
+
+        <div class="facturissynctab-wrap">
+
+          <input type="radio" id="facturissynctab1" name="facturissynctabGroup1" class="facturissynctab" checked>
+          <label for="facturissynctab1"><?php echo $tab_name_1;?></label>
+
+          <input type="radio" id="facturissynctab2" name="facturissynctabGroup1" class="facturissynctab">
+          <label for="facturissynctab2"><?php echo $tab_name_2;?></label>
+
+          <input type="radio" id="facturissynctab3" name="facturissynctabGroup1" class="facturissynctab">
+          <label for="facturissynctab3"><?php echo $tab_name_3;?></label>
+
+          <input type="radio" id="facturissynctab4" name="facturissynctabGroup1" class="facturissynctab">
+          <label for="facturissynctab4"><?php echo $tab_name_4;?></label>
+
+          <input type="radio" id="facturissynctab5" name="facturissynctabGroup1" class="facturissynctab">
+          <label for="facturissynctab5"><?php echo $tab_name_5;?></label>
+
+          <div class="facturissynctab__content">
+            <?php include_once('tab1.php')?>
+          </div>
+
+          <div class="facturissynctab__content">
+            <?php include_once('tab2.php')?>
+          </div>
+
+          <div class="facturissynctab__content">
+            <?php include_once('tab3.php')?>
+          </div>
+
+          <div class="facturissynctab__content">
+            <?php include_once('tab4.php')?>
+          </div>
+
+          <div class="facturissynctab__content">
+            <?php include_once('tab5.php')?>
+          </div>
+
+        </div>
+
+      </div>
+      <p style="text-align: center;"><a href="<?php echo $version_link; ?>" target="_blank" ><?php echo $version_text; ?>: <?php echo $version_number; ?></a></p>
+      <div style="text-align:center">
+        <small>
+          <a href="javascript: void(0)" style="color:#528ecc !important" onclick="checkLatestVersion();" ><?php echo $check_latest_version; ?></a>
+        </small>
+      </div>
+      <p style="text-align: center;" id="check_latest_version_result"></p>
+    </div>
+    <script type="text/javascript" src="View/js/tab1.js"></script>
+    <script type="text/javascript" src="View/js/tab2.js"></script>
+    <script type="text/javascript" src="View/js/tab3.js"></script>
+    <script type="text/javascript" src="View/js/tab4.js"></script>
+    <script type="text/javascript" src="View/js/tab5.js"></script>
+    <script type="text/javascript" src="View/vanillajs-datepicker/dist/js/datepicker-full.min.js"></script>
+    <script>
+      getPdlGestiuni();
+      showOrHideProformaSerie();
+
+      const elem = document.getElementById('datepicker-container');
+      const rangepicker = new DateRangePicker(elem, {
+        autohide: true,
+        todayHighlight :true,
+        maxDate : 'today',
+        format: 'dd.mm.yyyy',
+        clearBtn: true,
+        daysOfWeekHighlighted: [0,6],
+        defaultViewDate: 'today'
+      }); 
+
+      document.getElementById('input-date-start').value = '';
+      
+    </script>
+  </body>
+<html>

+ 27 - 0
View/tab1.php

@@ -0,0 +1,27 @@
+<form id="sync_form"  method="POST">
+
+    <header>
+        <h2><?php echo $tab_name_1;?></h2>
+        <div class="center"><?php echo $tab_description_1; ?></div>
+    </header>
+    
+    <div>
+        <label class="desc" id="sync_choice_label" for="sync_choice">
+            <?php //echo $select_text;?>
+        </label>
+        <div>
+            <select id="fsync_datatype" name="fsync_datatype" class="field select medium"> 
+                <?php foreach($dataTypes as $dataType): ?>
+                    <option value='<?php echo $dataType['id']; ?>'><?php echo $dataType['label']; ?></option>
+                <?php endforeach; ?>
+            </select>
+            <div id="fsync-datatype-error" class="red hide"><small><?php echo $datatype_error;?></small></div>
+            <div id="fsync-authform-error" class="red hide"><small><?php echo $authform_error;?></small></div>
+        </div>
+    </div>
+    
+    <div class="center">
+        <a id="btn_sync_form" href="javascript: void(0)" onclick="submitSyncForm();" class="btn btn-blue"><?php echo $sync_btn;?></a>
+    </div>
+    
+</form>

+ 53 - 0
View/tab2.php

@@ -0,0 +1,53 @@
+<form id="auth_form" method="POST">
+
+    <header>
+        <h2><?php echo $tab_name_2;?></h2>
+        <div class="center"><?php echo $tab_description_2; ?></div>
+    </header>
+    
+    <div>
+        <label class="desc" id="fsync_auth_apikey_label" for="fsync_auth_apikey"><?php echo $auth_apikey;?></label>
+        <div>
+        <input id="fsync_auth_apikey" name="fsync_auth_apikey" type="text" class="field text fn" value="<?php echo $auth['fsync_auth_apikey'];?>">
+        <div id="fsync_auth_apikey_error" class="red hide"><small><?php echo $auth_apikey_error;?></small></div>
+        </div>
+    </div>
+
+    <div>
+        <label class="desc" id="fsync_auth_fiscalcode_label" for="fsync_auth_fiscalcode"><?php echo $auth_fiscalcode;?></label>
+        <div>
+        <input id="fsync_auth_fiscalcode" name="fsync_auth_fiscalcode" type="text" class="field text fn" value="<?php echo $auth['fsync_auth_fiscalcode'];?>">
+        <div id="fsync_auth_fiscalcode_error" class="red hide"><small><?php echo $auth_fiscalcode_error;?></small></div>
+        </div>
+    </div>
+
+    <div>
+        <label class="desc" id="fsync_auth_username_label" for="fsync_auth_username"><?php echo $auth_username;?></label>
+        <div>
+        <input id="fsync_auth_username" name="fsync_auth_username" type="text" class="field text fn" value="<?php echo $auth['fsync_auth_username'];?>" >
+        <div id="fsync_auth_username_error" class="red hide"><small><?php echo $auth_username_error;?></small></div>
+        </div>
+    </div>
+
+    <div>
+        <label class="desc" id="fsync_auth_password_label" for="fsync_auth_password"><?php echo $auth_password;?></label>
+        <div>
+        <input id="fsync_auth_password" name="fsync_auth_password" type="password" class="field text fn" value="<?php echo $auth['fsync_auth_password'];?>">
+        <div id="fsync_auth_password_error" class="red hide"><small><?php echo $auth_password_error;?></small></div>
+        </div>
+    </div>
+
+    <div class="center red hide" id="authform_error">
+        <small><?php echo $authform_error;?></small>
+    </div>  
+
+    <div class="center">
+        <input type="hidden" name="action" value="save" />
+    </div>
+
+    <div class="center">
+        <a id="save-auth-btn" href="javascript: void(0)" onclick="submitAuthForm();" class="btn btn-green"><?php echo $save_btn;?></a>
+        <img src="View/img/loader.gif" style="position: relative;left: 10px;top: 3px;display:none;" id="save-loader"/>
+    </div>
+    
+</form>

+ 98 - 0
View/tab3.php

@@ -0,0 +1,98 @@
+<form id="order_option_form" method="POST">
+
+    <header>
+        <h2><?php echo $tab_name_3_1;?></h2>
+    </header>
+    
+    <div>
+        <fieldset>
+        
+        <legend id="sync_orders_as_legend" class="desc">
+            <?php echo $sync_orders_as; ?>
+        </legend>
+        
+        <div>
+            <?php foreach($syncOrdersAs as $syncOrdersAsElement): ?>
+                <div>
+                    <input id="fsync_option_syncordersas<?php echo $syncOrdersAsElement['id']; ?>" onclick="showOrHideProformaSerie();" class="fsync_option_syncordersas" name="fsync_option_syncordersas" type="radio" value="<?php echo $syncOrdersAsElement['id']; ?>" 
+                        <?php if($syncOrdersAsElement['id'] == $option['fsync_option_syncordersas']): ?>
+                            checked="checked"
+                        <?php endif; ?>
+                    >
+                    <label class="choice" for="fsync_option_syncordersas<?php echo $syncOrdersAsElement['id'];?>"><?php echo $syncOrdersAsElement['label']; ?></label>
+                </div>
+            <?php endforeach; ?>
+        </div>
+        </fieldset>
+    </div>
+
+    <div>
+        <label class="desc" id="fsync_option_proformaserie_label" for="fsync_option_proformaserie"><?php echo $proforma_series;?></label>
+        <div>
+            <input id="fsync_option_proformaserie" name="fsync_option_proformaserie" type="text" class="field text fn" value="<?php echo $option['fsync_option_proformaserie']; ?>">
+            <div id="fsync_option_proformaserie_error" class="red hide"><small><?php echo $option_proformaserie_error;?></small></div>
+        </div>
+    </div>
+
+    <div>
+        <label class="desc" id="fsync_option_daysago_label" for="fsync_option_daysago"><?php echo $days_ago;?></label>
+        <div>
+            <input id="fsync_option_daysago" name="fsync_option_daysago" type="text" class="field text fn" value="<?php echo $option['fsync_option_daysago']; ?>">
+            <div id="fsync_option_daysago_error" class="red hide"><small><?php echo $option_daysago_error;?></small></div>
+        </div>
+    </div>
+        
+    <div>
+        <fieldset>
+        
+        <legend id="auto_order_sync_legend" class="desc">
+            <?php echo $auto_order_sync; ?>
+        </legend>
+        
+        <div>
+            <?php foreach($autoSyncOptions as $autoSyncOptionsElement): ?>
+                <div>
+                    <input id="fsync_option_autosyncorder<?php echo $autoSyncOptionsElement['id']; ?>" name="fsync_option_autosyncorder" type="radio" value="<?php echo $autoSyncOptionsElement['id']; ?>" 
+                        <?php if($autoSyncOptionsElement['id'] == $option['fsync_option_autosyncorder']): ?>
+                            checked="checked"
+                        <?php endif; ?>
+                    >
+                    <label class="choice" for="fsync_option_autosyncorder<?php echo $autoSyncOptionsElement['id'];?>"><?php echo $autoSyncOptionsElement['label']; ?></label>
+                </div>
+            <?php endforeach; ?>
+        </div>
+        </fieldset>
+    </div>
+    
+    <div>
+        <fieldset>
+        
+        <legend id="" class="desc">
+            <?php echo $with_discount; ?> <br/><small class="text-muted"><?php echo $with_discount_desc; ?></small>
+        </legend>
+        
+        <div>
+            <?php foreach($withDiscountOptions as $withDiscountOptionsElement): ?>
+                <div>
+                    <input id="fsync_option_withdiscount<?php echo $withDiscountOptionsElement['id']; ?>" name="fsync_option_withdiscount" type="radio" value="<?php echo $withDiscountOptionsElement['id']; ?>" 
+                        <?php if($withDiscountOptionsElement['id'] == $option['fsync_option_withdiscount']): ?>
+                            checked="checked"
+                        <?php endif; ?>
+                    >
+                    <label class="choice" for="fsync_option_withdiscount<?php echo $withDiscountOptionsElement['id'];?>"><?php echo $withDiscountOptionsElement['label']; ?></label>
+                </div>
+            <?php endforeach; ?>
+        </div>
+        </fieldset>
+    </div>
+    
+    <div class="center">
+        <input type="hidden" name="action" value="save" />
+    </div>
+
+    <div class="center">
+        <a href="javascript: void(0)" onclick="submitOrderOptionForm();" class="btn btn-green" id="save-oo-btn" ><?php echo $save_btn;?></a>
+    </div>
+    
+</form>
+

+ 76 - 0
View/tab4.php

@@ -0,0 +1,76 @@
+<form id="stock_option_form" method="POST">
+
+    <header>
+        <h2><?php echo $tab_name_3_2;?></h2>
+    </header>
+    
+    <div>
+        <label class="desc" id="option_filterstock_legend" for="option_filterstock">
+            <?php echo $filter_stock;?>
+        </label>
+        <div>
+        <select id="fsync_option_filterstock" name="fsync_option_filterstock" class="field select medium"> 
+        <option value=""><?php echo $loading_data_please_wait; ?></option>
+        </select>
+        <input type="hidden" id="fsync_option_filterstock_hidden" value="<?php echo $option['fsync_option_filterstock'];?>" />
+        <input type="hidden" id="fsync_shop_hidden" value="<?php echo (isset($_REQUEST['shop']) ? $_REQUEST['shop'] : '');?>" />
+        </div>
+    </div>
+        
+    <div>
+        <fieldset>
+        
+        <legend id="auto_stock_sync_legend" class="desc">
+            <?php echo $auto_stock_sync; ?>
+        </legend>
+        
+        <div>
+            <?php foreach($autoSyncOptions as $autoSyncOptionsElement): ?>
+                <div>
+                    <input id="fsync_option_autosyncstock<?php echo $autoSyncOptionsElement['id']; ?>" name="fsync_option_autosyncstock" type="radio" value="<?php echo $autoSyncOptionsElement['id']; ?>" 
+                    <?php if($autoSyncOptionsElement['id'] == $option['fsync_option_autosyncstock']): ?>
+                        checked="checked"
+                    <?php endif; ?>
+                    >
+                    <label class="choice" for="fsync_option_autosyncstock<?php echo $autoSyncOptionsElement['id'];?>"><?php echo $autoSyncOptionsElement['label']; ?></label>
+                </div>
+            <?php endforeach; ?>
+        </div>
+        </fieldset>
+    </div>
+
+    <div>
+        <fieldset>
+        
+        <legend id="location_legend" class="desc">
+            <?php echo $locations; ?>
+        </legend>
+        
+        <div>
+            <?php foreach($shopLocations as $shopLocation): ?>
+                <div>
+                    <input id="fsync_option_location<?php echo $shopLocation['id']; ?>" name="fsync_option_locations[]" type="checkbox" value="<?php echo $shopLocation['id']; ?>" 
+                    <?php if(
+                        (is_array($option['fsync_option_locations']) && 
+                        in_array($shopLocation['id'], $option['fsync_option_locations'])) ||
+                        is_null($option['fsync_option_locations'])): ?>
+                        checked="checked"
+                    <?php endif; ?>
+                    >
+                    <label class="choice" for="fsync_option_location<?php echo $shopLocation['id'];?>"><?php echo $shopLocation['name']; ?></label>
+                </div>
+            <?php endforeach; ?>
+        </div>
+        </fieldset>
+    </div>
+    
+    <div class="center">
+        <input type="hidden" name="action" value="save" />
+    </div>
+
+    <div class="center">
+        <a href="javascript: void(0)" onclick="submitStockOptionForm();" class="btn btn-green" id="save-so-btn"><?php echo $save_btn;?></a>
+    </div>
+    
+</form>
+

+ 38 - 0
View/tab5.php

@@ -0,0 +1,38 @@
+<div style="text-align: center;">
+    <form class="form-horizontal" id="download_log_form" method="post">
+        <header>
+            <h3><?php echo $tab_name_5; ?></h3>
+            <small><?php echo $tab_description_5_1; ?></small>
+        </header>
+
+        <div id="datepicker-container">
+            <input type="text" name="start" id="input-date-start" autocomplete="off" value="">
+            <span> - </span>
+            <input type="text" name="end" id="input-date-stop" autocomplete="off" value="<?php echo date('d.m.Y'); ?>">  
+        </div>
+
+        <div class="form-group">
+            <a id="download_btn" href="javascript: void(0)" onclick="submitDownloadForm();" class="btn btn-green">
+                <i class="fa fa-download"></i> <?php echo $download_btn; ?>
+            </a>
+        </div>
+
+        <p id="fsync-download-1-error" class="red text-center" style="display:none;" ><?php echo $download_log_error_1; ?></p>
+        <p id="fsync-download-2-error" class="red text-center" style="display:none;" ><?php echo $download_log_error_2; ?></p>
+        <div class="center">
+            <input type="hidden" name="action" value="downloadLog" />
+        </div>
+    </form>
+</div>
+<div style="text-align: center;">
+    <hr/>
+    <form class="form-horizontal" id="clear_log_form" method="post">
+        <p><small><?php echo $tab_description_5_2; ?></small></p>
+        <a id="clear_btn"  href="javascript: void(0)" onclick="if(confirm('<?php echo $confirm_clear_log; ?>')) { clearLog(); }" class="btn btn-blue">
+            <?php echo $clear_btn; ?>
+        </a>
+        <div class="center">
+            <input type="hidden" name="action" value="clearLog" />
+        </div>
+    </form>
+</div>

+ 72 - 0
View/vanillajs-datepicker/CHANGELOG.md

@@ -0,0 +1,72 @@
+# changelog
+
+## 1.1.4
+
+### Changes
+
+- Chnage `datepicer.show()` to move the focus to the input field if it's not focused
+  - Fix #52 — picker shown by calling show() doesn't hide by clicking outside
+
+## 1.1.3
+
+### Bug fixes
+
+- Fix #51 - TypeError occurs when initial dates are set in the input filed in multidate mode
+
+## 1.1.2
+
+### Bug fixes
+
+- Fix #48 — inline picker submits form by click on prev/next buttons
+- Fix — date with the name of 30-day month is parsed incorrectly if the current date is the 31st
+
+## 1.1.1
+
+### Bug fixes
+
+- Fix #46 — date range picker cannot be created when using datepicker-full.min.js
+- Fix #45 - onClickOutside listener calls unfocus() when the input field is not focused
+
+## 1.1.0
+
+### New features
+
+- Add `updateOnBlur` option (#13)
+- Add `showOnClick` option (#21)
+    - Along with this, picker element's click handler is changed to keep the focus on input field after auto-hiding on date selection
+- Add `pickLevel` option (#22, #23)
+    - minView feature + comprehensive control on date picking level that works with edit on input field and `setDate()` call as well
+- Add optional `forceRender` argument to `refresh()`API
+- Add `setDates()` API to DateRangePicker (#27)
+- Add support for package entry points 
+
+### Bug fixes
+
+- Fix #33 — the view doesn't go back to the days view after changing the selection by other than mouse operation
+
+### Changes
+
+- Change the edit mode so that it no longer discards unparsed changes when exiting
+- Add shift + arrow key to the key patterns to enter the edit mode
+- Make range highlight between range-start and -end available on all views as well as the days view
+- Revise the cross reference between DateRangePicker and Datepicker instances to make it securely usable in custom event handler, etc.
+- Improve readability of selected date in previous/next month area in the calendar
+
+
+## 1.0.3
+
+- Fix #24 — change event was fired inappropriately through setDate() API call 
+
+## 1.0.2
+
+- Fix #11, #17, #19 — calendar wasn't redrawn properly in some conditions
+- Fix #3 — keyboard showed up by clicking on a calendar element when disableTouchKeyboard = true
+
+## 1.0.1
+
+- Add stylesheet for Foundation
+- Add support for importing js by package name (For rollup, webpack)
+
+## 1.0
+
+First release

+ 21 - 0
View/vanillajs-datepicker/LICENSE

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

+ 31 - 0
View/vanillajs-datepicker/README.md

@@ -0,0 +1,31 @@
+# Vanilla JS Datepicker
+
+A vanilla JavaScript remake of [bootstrap-datepicker](https://github.com/uxsolutions/bootstrap-datepicker) for [Bulma](https://bulma.io) and other CSS frameworks  
+
+This package is written from scratch as ECMAScript modules/[Sass](https://sass-lang.com) stylesheets to reproduce similar usability to bootstrap-datepicker.  
+It can work either standalone or with CSS framework (e.g. [Bootstrap](https://getbootstrap.com), [Foundation](https://get.foundation)), but works best with [Bulma](https://bulma.io) as it's developed primarily for Bulma. 
+
+The package also includes pre-built js/css files for those who like to use it directly on browser.
+
+### Features
+
+- Date picker (input-dropdown, inline), date range picker
+- Keyboard operation support (navigation by arrow keys, editing on input field)
+- i18n support (locales, CSS-based text direction detection)
+- Easily customizable to adapt stylesheet for various CSS frameworks
+- Dependency free
+- Made for modern browsers — no IE support
+- Lightweight (well, relatively…) — 33kB (minified, uncompressed)
+
+### Demo
+
+[Live Online Demo](https://raw.githack.com/mymth/vanillajs-datepicker/v1.1.4/demo/)
+
+### Documentation
+
+[Online Docs](https://mymth.github.io/vanillajs-datepicker)
+
+
+## License
+
+- [MIT](./LICENSE)

+ 538 - 0
View/vanillajs-datepicker/demo/bs4.html

@@ -0,0 +1,538 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <title>Vanilla JS Datepicker Demo</title>
+    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
+    <link rel="stylesheet" type="text/css" href="../dist/css/datepicker-bs4.css">
+    <script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
+    <style type="text/css">
+      main .section {
+        padding: 3rem 1.5rem;
+      }
+      aside {
+        position: fixed;
+        top: 0;
+        bottom: 0;
+        right: -300px;
+        width: 300px;
+        overflow: auto;
+        background-color: #fff;
+        box-shadow: inset 1px 1px rgba(0, 0, 0, 10%);
+        transition: right 0.3s;
+      }
+      .open aside {
+        right: 0;
+      }
+      aside hr {
+        margin-top: 0.5rem;
+      }
+      .code-wrap {
+        position: relative;
+      }
+      .code-wrap pre {
+        background-color: whitesmoke;
+        padding: 1.25rem 1.5rem;
+      }
+      .code-wrap pre:not(.is-active) {
+        height: 0;
+        overflow: hidden;
+        opacity: 0.5;
+      }
+      .code-wrap .collapse-button {
+        position: absolute;
+        top: 0;
+        right: 0;
+        left: auto;
+        cursor: pointer;
+        padding: 0.125rem 0.25rem;
+        font-size: 0.75rem;
+      }
+
+      .toggle-btn {
+        position: fixed;
+        top: 0.75rem;
+        right: 0.75rem;
+        width: 1.5rem;
+        background-color: #fff;
+        line-height: 1.5rem;
+        border: 1px solid rgba(0, 0, 0, 10%);
+        border-radius: 2px;
+        box-shadow: 1px 1px rgba(0, 0, 0, 10%);
+        cursor: pointer;
+      }
+      .toggle-btn::before {
+        content: '\25c0';
+        padding-left: 0.25rem;
+      }
+      .open .toggle-btn::before {
+        content: '\25b6';
+      }
+
+      @media (min-width: 481px) {
+        main {
+          margin-right: 38.1966%;
+        }
+        aside {
+          right: 0;
+          width: 38.1966%;
+        }
+        .toggle-btn {
+          display: none;
+        }
+      }
+    </style>
+  </head>
+  <body>
+    <main>
+      <section class="section container-fluid">
+        <div class="row">
+          <div class="col">
+            <p>Vanilla JS Datepicker</p>
+            <h1 class="title">Demo</h1>
+            <div id="sandbox"></div>
+          </div>
+        </div>
+        <div class="row">
+          <div class="col">
+            <p><small>Style: <a href="index.html">Bulma</a> | <em>Bootstrap</em> | <a href="foundation.html">Foundation</a> | <a href="plain-css.html">Plain CSS</a></small></p>
+          </div>
+        </div>
+      </section>
+    </main>
+
+    <aside>
+      <section class="section">
+        <div class="card">
+          <div class="card-body">
+            <h4 class="subtitle">Type</h4>
+            <form id="types">
+              <div class="form-group">
+                <div class="form-check form-check-inline">
+                  <input type="radio" class="form-check-input" id="type-input" name="type" value="input" checked>
+                  <label for="type-input" class="form-check-label">Input</label>
+                </div>
+                <div class="form-check form-check-inline">
+                  <input type="radio" class="form-check-input" id="type-inline" name="type" value="inline">
+                  <label for="type-inline" class="form-check-label">Inline</label>
+                </div>
+                <div class="form-check form-check-inline">
+                  <input type="radio" class="form-check-input" id="type-range" name="type" value="range">
+                  <label for="type-range" class="form-check-label">Range</label>
+                </div>
+              </div>
+            </form>
+          </div>
+          <hr>
+          <div class="card-body">
+            <h4 class="subtitle">Options</h4>
+            <form id="options">
+
+              <div class="form-group form-check" data-toggle="tooltip" title="Only effective in range picker">
+                <input type="checkbox" class="form-check-input" id="allowOneSidedRange" name="allowOneSidedRange" value="true">
+                <label for="allowOneSidedRange" class="form-check-label">allowOneSidedRange</label>
+              </div>
+
+              <div class="form-group form-check">
+                <input type="checkbox" class="form-check-input" id="autohide" name="autohide" value="true">
+                <label for="autohide" class="form-check-label">autohide</label>
+              </div>
+
+              <div class="form-group form-check">
+                <input type="checkbox" class="form-check-input" id="beforeShowDay" name="beforeShowDay" value="true">
+                <label for="beforeShowDay" class="form-check-label">beforeShowDay</label>
+                <div class="code-wrap">
+                  <pre id="code-beforeShowDay"></pre>
+                  <div class="collapse-button" data-target="code-beforeShowDay">show/hide</div>
+                </div>
+              </div>
+
+              <div class="form-group form-check">
+                <input type="checkbox" class="form-check-input" id="beforeShowMonth" name="beforeShowMonth" value="true">
+                <label for="beforeShowMonth" class="form-check-label">beforeShowMonth</label>
+                <div class="code-wrap">
+                  <pre id="code-beforeShowMonth"></pre>
+                  <div class="collapse-button" data-target="code-beforeShowMonth">show/hide</div>
+                </div>
+              </div>
+
+              <div class="form-group form-check">
+                <input type="checkbox" class="form-check-input" id="beforeShowYear" name="beforeShowYear" value="true">
+                <label for="beforeShowYear" class="form-check-label">beforeShowYear</label>
+                <div class="code-wrap">
+                  <pre id="code-beforeShowYear"></pre>
+                  <div class="collapse-button" data-target="code-beforeShowYear">show/hide</div>
+                </div>
+              </div>
+
+              <div class="form-group form-check">
+                <input type="checkbox" class="form-check-input" id="beforeShowDecade" name="beforeShowDecade" value="true">
+                <label for="beforeShowDecade" class="form-check-label">beforeShowDecade</label>
+                <div class="code-wrap">
+                  <pre id="code-beforeShowDecade"></pre>
+                  <div class="collapse-button" data-target="code-beforeShowDecade">show/hide</div>
+                </div>
+              </div>
+
+              <div class="form-group form-check">
+                <input type="checkbox" class="form-check-input" id="calendarWeeks" name="calendarWeeks" value="true">
+                <label for="calendarWeeks" class="form-check-label">calendarWeeks</label>
+              </div>
+
+              <div class="form-group form-check">
+                <input type="checkbox" class="form-check-input" id="clearBtn" name="clearBtn" value="true">
+                <label for="clearBtn" class="form-check-label">clearBtn</label>
+              </div>
+
+              <div class="form-group">
+                <label class="label">dateDelimiter</label>
+                <input type="text" class="form-control" name="dateDelimiter" placeholder=",">
+              </div>
+
+              <div class="form-group" data-toggle="tooltip" title="enter in JSON format">
+                <label class="label">datesDisabled</label>
+                <textarea class="form-control" name="datesDisabled" placeholder="[]"></textarea>
+                <div class="invalid-feedback"></div>
+              </div>
+
+              <div class="form-group">
+                <label for="daysOfWeekDisabled" class="label">daysOfWeekDisabled</label>
+                <div>
+                  <div class="form-check form-check-inline">
+                    <input type="checkbox" class="form-check-input" id="daysOfWeekDisabled-0" name="daysOfWeekDisabled" value="0">
+                    <label for="daysOfWeekDisabled-0" class="form-check-label">0</label>&nbsp;
+                  </div>
+                  <div class="form-check form-check-inline">
+                    <input type="checkbox" class="form-check-input" id="daysOfWeekDisabled-1" name="daysOfWeekDisabled" value="1">
+                    <label for="daysOfWeekDisabled-1" class="form-check-label">1</label>&nbsp;
+                  </div>
+                  <div class="form-check form-check-inline">
+                    <input type="checkbox" class="form-check-input" id="daysOfWeekDisabled-2" name="daysOfWeekDisabled" value="2">
+                    <label for="daysOfWeekDisabled-2" class="form-check-label">2</label>&nbsp;
+                  </div>
+                  <div class="form-check form-check-inline">
+                    <input type="checkbox" class="form-check-input" id="daysOfWeekDisabled-3" name="daysOfWeekDisabled" value="3">
+                    <label for="daysOfWeekDisabled-3" class="form-check-label">3</label>&nbsp;
+                  </div>
+                  <div class="form-check form-check-inline">
+                    <input type="checkbox" class="form-check-input" id="daysOfWeekDisabled-4" name="daysOfWeekDisabled" value="4">
+                    <label for="daysOfWeekDisabled-4" class="form-check-label">4</label>&nbsp;
+                  </div>
+                  <div class="form-check form-check-inline">
+                    <input type="checkbox" class="form-check-input" id="daysOfWeekDisabled-5" name="daysOfWeekDisabled" value="5">
+                    <label for="daysOfWeekDisabled-5" class="form-check-label">5</label>&nbsp;
+                  </div>
+                  <div class="form-check form-check-inline">
+                    <input type="checkbox" class="form-check-input" id="daysOfWeekDisabled-6" name="daysOfWeekDisabled" value="6">
+                    <label for="daysOfWeekDisabled-6" class="form-check-label">6</label>&nbsp;
+                  </div>
+                </div>
+              </div>
+
+              <div class="form-group">
+                <label for="daysOfWeekHighlighted" class="label">daysOfWeekHighlighted</label>
+                <div>
+                  <div class="form-check form-check-inline">
+                    <input type="checkbox" class="form-check-input" id="daysOfWeekHighlighted-0" name="daysOfWeekHighlighted" value="0">
+                    <label for="daysOfWeekHighlighted-0" class="form-check-label">0</label>&nbsp;
+                  </div>
+                  <div class="form-check form-check-inline">
+                    <input type="checkbox" class="form-check-input" id="daysOfWeekHighlighted-1" name="daysOfWeekHighlighted" value="1">
+                    <label for="daysOfWeekHighlighted-1" class="form-check-label">1</label>&nbsp;
+                  </div>
+                  <div class="form-check form-check-inline">
+                    <input type="checkbox" class="form-check-input" id="daysOfWeekHighlighted-2" name="daysOfWeekHighlighted" value="2">
+                    <label for="daysOfWeekHighlighted-2" class="form-check-label">2</label>&nbsp;
+                  </div>
+                  <div class="form-check form-check-inline">
+                    <input type="checkbox" class="form-check-input" id="daysOfWeekHighlighted-3" name="daysOfWeekHighlighted" value="3">
+                    <label for="daysOfWeekHighlighted-3" class="form-check-label">3</label>&nbsp;
+                  </div>
+                  <div class="form-check form-check-inline">
+                    <input type="checkbox" class="form-check-input" id="daysOfWeekHighlighted-4" name="daysOfWeekHighlighted" value="4">
+                    <label for="daysOfWeekHighlighted-4" class="form-check-label">4</label>&nbsp;
+                  </div>
+                  <div class="form-check form-check-inline">
+                    <input type="checkbox" class="form-check-input" id="daysOfWeekHighlighted-5" name="daysOfWeekHighlighted" value="5">
+                    <label for="daysOfWeekHighlighted-5" class="form-check-label">5</label>&nbsp;
+                  </div>
+                  <div class="form-check form-check-inline">
+                    <input type="checkbox" class="form-check-input" id="daysOfWeekHighlighted-6" name="daysOfWeekHighlighted" value="6">
+                    <label for="daysOfWeekHighlighted-6" class="form-check-label">6</label>&nbsp;
+                  </div>
+                </div>
+              </div>
+
+              <div class="form-group">
+                <label class="label">defaultViewDate</label>
+                <input type="text" class="form-control" name="defaultViewDate" placeholder="today">
+              </div>
+
+              <div class="form-group form-check">
+                <input type="checkbox" class="form-check-input" id="disableTouchKeyboard" name="disableTouchKeyboard" value="true">
+                <label for="disableTouchKeyboard" class="form-check-label">disableTouchKeyboard</label>
+              </div>
+
+              <div class="form-group">
+                <label class="label">format</label>
+                <input type="text" class="form-control" name="format" placeholder="mm/dd/yyyy">
+              </div>
+
+              <div class="form-group">
+                <label class="label">language</label>
+                <select class="form-control" name="language" >
+                  <option value="en">en – English (US)</option>
+                </select>
+              </div>
+
+              <div class="form-group">
+                <label class="label">maxDate</label>
+                <input type="text" class="form-control" name="maxDate" placeholder="null">
+              </div>
+
+              <div class="form-group" data-toggle="tooltip" title="Not effective in range picker">
+                <label class="label">maxNumberOfDates</label>
+                <input type="text" class="form-control" name="maxNumberOfDates" placeholder="1">
+              </div>
+
+              <div class="form-group">
+                <label class="label">maxView</label>
+                <select class="form-control" name="maxView">
+                  <option value="0">0 – days</option>
+                  <option value="1">1 – months</option>
+                  <option value="2">2 – years</option>
+                  <option value="3" selected>3 – decades</option>
+                </select>
+              </div>
+
+              <div class="form-group">
+                <label class="label">minDate</label>
+                <input type="text" class="form-control" name="minDate" placeholder="null">
+              </div>
+
+              <div class="form-group">
+                <label class="label">nextArrow</label>
+                <textarea class="form-control" name="nextArrow" placeholder="»"></textarea>
+              </div>
+
+              <div class="form-group">
+                <label class="label">orientation</label>
+                <select class="form-control" name="orientation" >
+                  <option value="auto">auto</option>
+                  <option value="top auto">top auto</option>
+                  <option value="bottom auto">bottom auto</option>
+                  <option value="auto left">auto left</option>
+                  <option value="top left">top left</option>
+                  <option value="bottom left">bottom left</option>
+                  <option value="auto right">auto right</option>
+                  <option value="top right">top right</option>
+                  <option value="bottom right">bottom right</option>
+                </select>
+              </div>
+
+              <div class="form-group">
+                <label class="label">pickLevel</label>
+                <select class="form-control" name="pickLevel">
+                  <option value="0">0 – date</option>
+                  <option value="1">1 – month</option>
+                  <option value="2">2 – year</option>
+                </select>
+              </div>
+
+              <div class="form-group">
+                <label class="label">prevArrow</label>
+                <textarea class="form-control" name="prevArrow" placeholder="«"></textarea>
+              </div>
+
+              <div class="form-group form-check">
+                <input type="checkbox" class="form-check-input" id="showDaysOfWeek" name="showDaysOfWeek" value="true" checked>
+                <label for="showDaysOfWeek" class="form-check-label">showDaysOfWeek</label>
+              </div>
+
+              <div class="form-group form-check">
+                <input type="checkbox" class="form-check-input" id="showOnClick" name="showOnClick" value="true" checked>
+                <label for="showOnClick" class="form-check-label">showOnClick</label>
+              </div>
+
+              <div class="form-group form-check">
+                <input type="checkbox" class="form-check-input" id="showOnFocus" name="showOnFocus" value="true" checked>
+                <label for="showOnFocus" class="form-check-label">showOnFocus</label>
+              </div>
+
+              <div class="form-group">
+                <label class="label">startView</label>
+                <select class="form-control" name="startView">
+                  <option value="0">0 – days</option>
+                  <option value="1">1 – months</option>
+                  <option value="2">2 – years</option>
+                  <option value="3">3 – decades</option>
+                </select>
+              </div>
+
+              <div class="form-group">
+                <label class="label">title</label>
+                <input type="text" class="form-control" name="title">
+              </div>
+
+              <div class="form-group form-check">
+                <input type="checkbox" class="form-check-input" id="todayBtn" name="todayBtn" value="true">
+                <label for="todayBtn" class="form-check-label">todayBtn</label>
+              </div>
+
+              <div class="form-group">
+                <label class="label">todayBtnMode</label>
+                <select class="form-control" name="todayBtnMode">
+                  <option value="0">0 – focus</option>
+                  <option value="1">1 – select</option>
+                </select>
+              </div>
+
+              <div class="form-group form-check">
+                <input type="checkbox" class="form-check-input" id="todayHighlight" name="todayHighlight" value="true">
+                <label for="todayHighlight" class="form-check-label">todayHighlight</label>
+              </div>
+
+              <div class="form-group form-check">
+                <input type="checkbox" class="form-check-input" id="updateOnBlur" name="updateOnBlur" value="true" checked>
+                <label for="updateOnBlur" class="form-check-label">updateOnBlur</label>
+              </div>
+
+              <div class="form-group">
+                <label class="label">weekStart</label>
+                <input type="text" class="form-control" name="weekStart" placeholder="0">
+              </div>
+
+            </form>
+          </div>
+          <hr>
+          <div class="card-body">
+            <h4 class="subtitle">Text direction</h4>
+            <form id="direction">
+              <div class="form-group">
+                <div class="form-check form-check-inline">
+                  <input type="radio" class="form-check-input" id="direction-ltr" name="direction" value="ltr" checked>
+                  <label for="direction-ltr" class="form-check-label">LTR</label>
+                </div>
+                <div class="form-check form-check-inline">
+                  <input type="radio" class="form-check-input" id="direction-rtl" name="direction" value="rtl">
+                  <label for="direction-rtl" class="form-check-label">RTL</label>
+                </div>
+              </div>
+            </form>
+          </div>
+        </div>
+      </section>
+    </aside>
+
+    <div class="toggle-btn"></div>
+    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
+    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
+    <script src="../dist/js/datepicker-full.js"></script>
+    <script src="./live-demo.js"></script>
+    <script>
+      /*global $ initialize onChangeType onChnageDirection onChangeInputOption onChangeInputOption onChangeTextareaOption onClickCheckboxOptions switchPicker */
+      window.templates = {
+        input: `<div class="form-group"><input type="text" class="form-control date"></div>`,
+        inline: `<div class="date"></div>`,
+        range: `<div class="d-flex flex-row date input-daterange">
+  <div class="form-group">
+    <input type="text" name="range-start" class="form-control">
+  </div>
+  <div class="flex-grow-0">
+    <a class="btn" disabled>to</a>
+  </div>
+  <div class="form-group">
+    <input type="text" name="range-end" class="form-control">
+  </div>
+</div>`,
+      };
+      window.beforeShowFns = {
+beforeShowDay(date) {
+  if (date.getMonth() == new Date().getMonth()) {
+    switch (date.getDate()) {
+      case 4:
+        return {
+          content: '<span data-toggle="tooltip" title="Example tooltip">4</span>',
+          classes: 'bg-info'
+        };
+      case 8:
+        return false;
+      case 12:
+        return "text-success";
+    }
+  }
+},
+beforeShowMonth(date) {
+  switch (date.getMonth()) {
+    case 6:
+      if (date.getFullYear() === new Date().getFullYear()) {
+        return {content: '🎉'};
+      }
+      break;
+    case 8:
+      return false;
+  }
+},
+beforeShowYear(date) {
+  switch (date.getFullYear()) {
+    case 2017:
+      return false;
+    case 2020:
+      return {content: '<span data-toggle="tooltip" data-placement="bottom" title="Tooltip text">2020</span>'};
+  }
+},
+beforeShowDecade(date) {
+  switch (date.getFullYear()) {
+    case 2000:
+      return false;
+    case 2100:
+      return {
+        content: '💯',
+        classes: 'bg-success',
+      };
+  }
+},
+      };
+      window.buttonClass = 'btn';
+      window.addError = function addError(el, message) {
+        el.classList.add('is-invalid');
+        el.parentElement.querySelector('.invalid-feedback').textContent = message;
+      }
+      window.removeErrors = function removeErrors(el) {
+        el.classList.remove('is-invalid');
+        el.parentElement.querySelector('.invalid-feedback').textContent = '';
+      }
+
+      initialize();
+
+      document.getElementById('types').querySelectorAll('input').forEach((el) => {
+        el.addEventListener('change', onChangeType);
+      });
+
+      document.getElementById('direction').querySelectorAll('input').forEach((el) => {
+        el.addEventListener('change', onChnageDirection);
+      });
+
+      const optsForm = document.getElementById('options');
+      optsForm.querySelectorAll('input[type=text], input[type=radio], select').forEach((el) => {
+        el.addEventListener('change', onChangeInputOption);
+      });
+      optsForm.querySelectorAll('textarea').forEach((el) => {
+        el.addEventListener('change', onChangeTextareaOption);
+      });
+      optsForm.querySelectorAll('input[type=checkbox]').forEach((el) => {
+        el.addEventListener('click', onClickCheckboxOptions);
+      });
+
+      switchPicker('input');
+
+      const initTooltips = () => {
+        $('[data-toggle="tooltip"]').tooltip();
+      };
+      setInterval(initTooltips, 1000);
+      initTooltips();
+    </script>
+  </body>
+</html>

+ 599 - 0
View/vanillajs-datepicker/demo/foundation.html

@@ -0,0 +1,599 @@
+<!DOCTYPE html>
+<html class="no-js">
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <title>Vanilla JS Datepicker Demo</title>
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/foundation-sites@6.4.3/dist/css/foundation.min.css" integrity="sha256-GSio8qamaXapM8Fq9JYdGNTvk/dgs+cMLgPeevOYEx0= sha384-wAweiGTn38CY2DSwAaEffed6iMeflc0FMiuptanbN4J+ib+342gKGpvYRWubPd/+ sha512-QHEb6jOC8SaGTmYmGU19u2FhIfeG+t/hSacIWPpDzOp5yygnthL3JwnilM7LM1dOAbJv62R+/FICfsrKUqv4Gg==" crossorigin="anonymous">
+    <link rel="stylesheet" type="text/css" href="../dist/css/datepicker-foundation.css">
+    <script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
+    <style type="text/css">
+      main .section {
+        padding: 3rem 1.5rem;
+      }
+      aside {
+        position: fixed;
+        top: 0;
+        bottom: 0;
+        right: -300px;
+        width: 300px;
+        overflow: auto;
+        background-color: #fff;
+        box-shadow: inset 1px 1px rgba(0, 0, 0, 10%);
+        transition: right 0.3s;
+      }
+      .open aside {
+        right: 0;
+      }
+      aside hr {
+        margin-top: 0.5rem;
+      }
+      .code-wrap {
+        position: relative;
+        margin-top: calc(-1rem + 1px);
+        margin-bottom: 1rem;
+      }
+      .code-wrap pre {
+        background-color: whitesmoke;
+        padding: 1.25rem 1.5rem;
+      }
+      .code-wrap pre:not(.is-active) {
+        height: 0;
+        overflow: hidden;
+        opacity: 0.5;
+      }
+      .code-wrap .collapse-button {
+        position: absolute;
+        top: 0;
+        right: 0;
+        left: auto;
+        cursor: pointer;
+        padding: 0.125rem 0.25rem;
+        font-size: 0.75rem;
+      }
+      .check-group {
+        display: flex;
+        flex-wrap: wrap;
+      }
+      .field.has-tip {
+        display: block;
+        border-bottom: 0;
+      }
+
+      .toggle-btn {
+        position: fixed;
+        top: 0.75rem;
+        right: 0.75rem;
+        width: 1.5rem;
+        background-color: #fff;
+        line-height: 1.5rem;
+        border: 1px solid rgba(0, 0, 0, 10%);
+        border-radius: 2px;
+        box-shadow: 1px 1px rgba(0, 0, 0, 10%);
+        cursor: pointer;
+      }
+      .toggle-btn::before {
+        content: '\25c0';
+        padding-left: 0.25rem;
+      }
+      .open .toggle-btn::before {
+        content: '\25b6';
+      }
+
+      @media (min-width: 481px) {
+        main {
+          margin-right: 38.1966%;
+        }
+        aside {
+          right: 0;
+          width: 38.1966%;
+        }
+        .toggle-btn {
+          display: none;
+        }
+      }
+
+      .text-success {
+        color: #3adb76 !important;
+      }
+      .text-warning {
+        color: #ffae00 !important;
+      }
+      .text-alert {
+        color: #cc4b37 !important;
+      }
+      .background-success {
+        background-color: #3adb76 !important;
+      }
+      .background-warning {
+        background-color: #ffae00 !important;
+      }
+      .background-alert {
+        background-color: #cc4b37 !important;
+      }
+    </style>
+  </head>
+  <body>
+    <main>
+      <section class="section container-fluid">
+        <div class="row">
+          <div class="columns">
+            <p>Vanilla JS Datepicker</p>
+            <h1 class="title">Demo</h1>
+            <div id="sandbox"></div>
+          </div>
+        </div>
+        <div class="row">
+          <div class="columns">
+            <p><small>Style: <a href="index.html">Bulma</a> | <a href="bs4.html">Bootstrap</a> | <em>Foundation</em> | <a href="plain-css.html">Plain CSS</a></small></p>
+          </div>
+        </div>
+      </section>
+    </main>
+
+    <aside>
+      <section class="section">
+        <div class="grid-x grid-padding-x grid-padding-y">
+          <div class="cell">
+            <h4 class="subtitle">Type</h4>
+            <form id="types">
+              <div class="field">
+                <input type="radio" id="type-input" name="type" value="input" checked>
+                <label for="type-input">Input</label>
+                <input type="radio" id="type-inline" name="type" value="inline">
+                <label for="type-inline">Inline</label>
+                <input type="radio" id="type-range" name="type" value="range">
+                <label for="type-range">Range</label>
+              </div>
+            </form>
+          </div>
+          <div class="cell">
+            <hr>
+            <h4 class="subtitle">Options</h4>
+            <form id="options">
+
+              <div class="field" data-tooltip title="Only effective in range picker">
+                <input type="checkbox" id="allowOneSidedRange" name="allowOneSidedRange" value="true">
+                <label for="allowOneSidedRange">allowOneSidedRange</label>
+              </div>
+
+              <div class="field">
+                <input type="checkbox" id="autohide" name="autohide" value="true">
+                <label for="autohide">autohide</label>
+              </div>
+
+              <div class="field">
+                <input type="checkbox" id="beforeShowDay" name="beforeShowDay" value="true">
+                <label for="beforeShowDay">beforeShowDay</label>
+                <div class="code-wrap">
+                  <pre id="code-beforeShowDay"></pre>
+                  <div class="collapse-button" data-target="code-beforeShowDay">show/hide</div>
+                </div>
+              </div>
+
+              <div class="field">
+                <input type="checkbox" id="beforeShowMonth" name="beforeShowMonth" value="true">
+                <label for="beforeShowMonth">beforeShowMonth</label>
+                <div class="code-wrap">
+                  <pre id="code-beforeShowMonth"></pre>
+                  <div class="collapse-button" data-target="code-beforeShowMonth">show/hide</div>
+                </div>
+              </div>
+
+              <div class="field">
+                <input type="checkbox" id="beforeShowYear" name="beforeShowYear" value="true">
+                <label for="beforeShowYear">beforeShowYear</label>
+                <div class="code-wrap">
+                  <pre id="code-beforeShowYear"></pre>
+                  <div class="collapse-button" data-target="code-beforeShowYear">show/hide</div>
+                </div>
+              </div>
+
+              <div class="field">
+                <input type="checkbox" id="beforeShowDecade" name="beforeShowDecade" value="true">
+                <label for="beforeShowDecade">beforeShowDecade</label>
+                <div class="code-wrap">
+                  <pre id="code-beforeShowDecade"></pre>
+                  <div class="collapse-button" data-target="code-beforeShowDecade">show/hide</div>
+                </div>
+              </div>
+
+              <div class="field">
+                <input type="checkbox" id="calendarWeeks" name="calendarWeeks" value="true">
+                <label for="calendarWeeks">calendarWeeks</label>
+              </div>
+
+              <div class="field">
+                <input type="checkbox" id="clearBtn" name="clearBtn" value="true">
+                <label for="clearBtn">clearBtn</label>
+              </div>
+
+              <div class="field">
+                <label>
+                  dateDelimiter
+                  <input type="text" name="dateDelimiter" placeholder=",">
+                </label>
+              </div>
+
+              <div class="field" data-tooltip title="enter in JSON format">
+                <label>
+                  datesDisabled
+                  <textarea name="datesDisabled" placeholder="[]"></textarea>
+                  <span class="form-error is-visible"></span>
+                </label>
+              </div>
+
+              <div class="field">
+                <label>daysOfWeekDisabled</label>
+                <div class="check-group">
+                  <div class="check-group-item">
+                    <input type="checkbox" id="daysOfWeekDisabled-0" name="daysOfWeekDisabled" value="0">
+                    <label for="daysOfWeekDisabled-0">0</label>&nbsp;
+                  </div>
+                  <div class="check-group-item">
+                    <input type="checkbox" id="daysOfWeekDisabled-1" name="daysOfWeekDisabled" value="1">
+                    <label for="daysOfWeekDisabled-1">1</label>&nbsp;
+                  </div>
+                  <div class="check-group-item">
+                    <input type="checkbox" id="daysOfWeekDisabled-2" name="daysOfWeekDisabled" value="2">
+                    <label for="daysOfWeekDisabled-2">2</label>&nbsp;
+                  </div>
+                  <div class="check-group-item">
+                    <input type="checkbox" id="daysOfWeekDisabled-3" name="daysOfWeekDisabled" value="3">
+                    <label for="daysOfWeekDisabled-3">3</label>&nbsp;
+                  </div>
+                  <div class="check-group-item">
+                    <input type="checkbox" id="daysOfWeekDisabled-4" name="daysOfWeekDisabled" value="4">
+                    <label for="daysOfWeekDisabled-4">4</label>&nbsp;
+                  </div>
+                  <div class="check-group-item">
+                    <input type="checkbox" id="daysOfWeekDisabled-5" name="daysOfWeekDisabled" value="5">
+                    <label for="daysOfWeekDisabled-5">5</label>&nbsp;
+                  </div>
+                  <div class="check-group-item">
+                    <input type="checkbox" id="daysOfWeekDisabled-6" name="daysOfWeekDisabled" value="6">
+                    <label for="daysOfWeekDisabled-6">6</label>&nbsp;
+                  </div>
+                </div>
+              </div>
+
+              <div class="field">
+                <label>daysOfWeekHighlighted</label>
+                <div class="check-group">
+                  <div class="check-group-item">
+                    <input type="checkbox" id="daysOfWeekHighlighted-0" name="daysOfWeekHighlighted" value="0">
+                    <label for="daysOfWeekHighlighted-0">0</label>&nbsp;
+                  </div>
+                  <div class="check-group-item">
+                    <input type="checkbox" id="daysOfWeekHighlighted-1" name="daysOfWeekHighlighted" value="1">
+                    <label for="daysOfWeekHighlighted-1">1</label>&nbsp;
+                  </div>
+                  <div class="check-group-item">
+                    <input type="checkbox" id="daysOfWeekHighlighted-2" name="daysOfWeekHighlighted" value="2">
+                    <label for="daysOfWeekHighlighted-2">2</label>&nbsp;
+                  </div>
+                  <div class="check-group-item">
+                    <input type="checkbox" id="daysOfWeekHighlighted-3" name="daysOfWeekHighlighted" value="3">
+                    <label for="daysOfWeekHighlighted-3">3</label>&nbsp;
+                  </div>
+                  <div class="check-group-item">
+                    <input type="checkbox" id="daysOfWeekHighlighted-4" name="daysOfWeekHighlighted" value="4">
+                    <label for="daysOfWeekHighlighted-4">4</label>&nbsp;
+                  </div>
+                  <div class="check-group-item">
+                    <input type="checkbox" id="daysOfWeekHighlighted-5" name="daysOfWeekHighlighted" value="5">
+                    <label for="daysOfWeekHighlighted-5">5</label>&nbsp;
+                  </div>
+                  <div class="check-group-item">
+                    <input type="checkbox" id="daysOfWeekHighlighted-6" name="daysOfWeekHighlighted" value="6">
+                    <label for="daysOfWeekHighlighted-6">6</label>&nbsp;
+                  </div>
+                </div>
+              </div>
+
+              <div class="field">
+                <label>
+                  defaultViewDate
+                  <input type="text" name="defaultViewDate" placeholder="today">
+                </label>
+              </div>
+
+              <div class="field">
+                <input type="checkbox" id="disableTouchKeyboard" name="disableTouchKeyboard" value="true">
+                <label for="disableTouchKeyboard">disableTouchKeyboard</label>
+              </div>
+
+              <div class="field">
+                <label>
+                  format
+                  <input type="text" name="format" placeholder="mm/dd/yyyy">
+                </label>
+              </div>
+
+              <div class="field">
+                <label>
+                  language
+                  <select name="language" >
+                    <option valUe="en">en – English (US)</option>
+                  </select>
+                </label>
+              </div>
+
+              <div class="field">
+                <label>
+                  maxDate
+                  <input type="text" name="maxDate" placeholder="null">
+                </label>
+              </div>
+
+              <div class="field" data-tooltip title="Not effective in range picker">
+                <label>
+                  maxNumberOfDates
+                  <input type="text" name="maxNumberOfDates" placeholder="1">
+                </label>
+              </div>
+
+              <div class="field">
+                <label>
+                  maxView
+                  <select name="maxView">
+                    <option value="0">0 – days</option>
+                    <option value="1">1 – months</option>
+                    <option value="2">2 – years</option>
+                    <option value="3" selected>3 – decades</option>
+                  </select>
+                </label>
+              </div>
+
+              <div class="field">
+                <label>
+                  minDate
+                  <input type="text" name="minDate" placeholder="null">
+                </label>
+              </div>
+
+              <div class="field">
+                <label>
+                  nextArrow
+                  <textarea name="nextArrow" placeholder="»"></textarea>
+                </label>
+              </div>
+
+              <div class="field">
+                <label>
+                  orientation
+                  <select name="orientation" >
+                    <option value="auto">auto</option>
+                    <option value="top auto">top auto</option>
+                    <option value="bottom auto">bottom auto</option>
+                    <option value="auto left">auto left</option>
+                    <option value="top left">top left</option>
+                    <option value="bottom left">bottom left</option>
+                    <option value="auto right">auto right</option>
+                    <option value="top right">top right</option>
+                    <option value="bottom right">bottom right</option>
+                  </select>
+                </label>
+              </div>
+
+              <div class="field">
+                <label>
+                  pickLevel
+                  <select name="pickLevel">
+                    <option value="0">0 – date</option>
+                    <option value="1">1 – month</option>
+                    <option value="2">2 – year</option>
+                  </select>
+                </label>
+              </div>
+
+              <div class="field">
+                <label>
+                  prevArrow
+                  <textarea name="prevArrow" placeholder="«"></textarea>
+                </label>
+              </div>
+
+              <div class="field">
+                <input type="checkbox" id="showDaysOfWeek" name="showDaysOfWeek" value="true" checked>
+                <label for="showDaysOfWeek">showDaysOfWeek</label>
+              </div>
+
+              <div class="field">
+                <input type="checkbox" id="showOnClick" name="showOnClick" value="true" checked>
+                <label for="showOnClick">showOnClick</label>
+              </div>
+
+              <div class="field">
+                <input type="checkbox" id="showOnFocus" name="showOnFocus" value="true" checked>
+                <label for="showOnFocus">showOnFocus</label>
+              </div>
+
+              <div class="field">
+                <label>
+                  startView
+                  <select name="startView">
+                    <option value="0">0 – days</option>
+                    <option value="1">1 – months</option>
+                    <option value="2">2 – years</option>
+                    <option value="3">3 – decades</option>
+                  </select>
+                </label>
+              </div>
+
+              <div class="field">
+                <label>
+                  title
+                  <input type="text" name="title">
+                </label>
+              </div>
+
+              <div class="field">
+                <input type="checkbox" id="todayBtn" name="todayBtn" value="true">
+                <label for="todayBtn">todayBtn</label>
+              </div>
+
+              <div class="field">
+                <label>
+                  todayBtnMode
+                  <select name="todayBtnMode">
+                    <option value="0">0 – focus</option>
+                    <option value="1">1 – select</option>
+                  </select>
+                </label>
+              </div>
+
+              <div class="field">
+                <input type="checkbox" id="todayHighlight" name="todayHighlight" value="true">
+                <label for="todayHighlight">todayHighlight</label>
+              </div>
+
+              <div class="field">
+                <input type="checkbox" id="updateOnBlur" name="updateOnBlur" value="true" checked>
+                <label for="updateOnBlur">updateOnBlur</label>
+              </div>
+
+              <div class="field">
+                <label>
+                  weekStart
+                  <input type="text" name="weekStart" placeholder="0">
+                </label>
+              </div>
+
+            </form>
+          </div>
+          <div class="cell">
+            <hr>
+            <h4 class="subtitle">Text direction</h4>
+            <form id="direction">
+              <div class="field">
+                <input type="radio" id="direction-ltr" name="direction" value="ltr" checked>
+                <label for="direction-ltr">LTR</label>
+                <input type="radio" id="direction-rtl" name="direction" value="rtl">
+                <label for="direction-rtl">RTL</label>
+              </div>
+            </form>
+          </div>
+        </div>
+      </section>
+    </aside>
+
+    <div class="toggle-btn"></div>
+    <script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/what-input/5.2.6/what-input.min.js"></script>
+    <script src="https://cdn.jsdelivr.net/npm/foundation-sites@6.4.3/dist/js/foundation.min.js" integrity="sha256-mRYlCu5EG+ouD07WxLF8v4ZAZYCA6WrmdIXyn1Bv9Vk= sha384-KzKofw4qqetd3kvuQ5AdapWPqV1ZI+CnfyfEwZQgPk8poOLWaabfgJOfmW7uI+AV sha512-0gHfaMkY+Do568TgjJC2iMAV0dQlY4NqbeZ4pr9lVUTXQzKu8qceyd6wg/3Uql9qA2+3X5NHv3IMb05wb387rA==" crossorigin="anonymous"></script>
+    <script src="../dist/js/datepicker-full.js"></script>
+    <script src="./live-demo.js"></script>
+    <script>
+      /*global $ initialize onChangeType onChnageDirection onChangeInputOption onChangeInputOption onChangeTextareaOption onClickcheckboxOptions switchPicker */
+      window.templates = {
+        input: `<div class="field"><input type="text" class="date"></div>`,
+        inline: `<div class="date"></div>`,
+        range: `<div class="input-group date input-daterange">
+  <input type="text" name="range-start" class="input-group-field">
+  <span class="input-group-label" disabled>to</span>
+  <input type="text" name="range-end" class="input-group-field">
+</div>`,
+      };
+      window.beforeShowFns = {
+beforeShowDay(date) {
+  if (date.getMonth() == new Date().getMonth()) {
+    switch (date.getDate()) {
+      case 4:
+        return {
+          content: '<span data-tooltip title="Example tooltip">4</span>',
+          classes: 'background-warning'
+        };
+      case 8:
+        return false;
+      case 12:
+        return "text-success";
+    }
+  }
+},
+beforeShowMonth(date) {
+  switch (date.getMonth()) {
+    case 6:
+      if (date.getFullYear() === new Date().getFullYear()) {
+        return {content: '🎉'};
+      }
+      break;
+    case 8:
+      return false;
+  }
+},
+beforeShowYear(date) {
+  switch (date.getFullYear()) {
+    case 2017:
+      return false;
+    case 2020:
+      return {content: '<span data-tooltip data-placement="bottom" title="Tooltip text">2020</span>'};
+  }
+},
+beforeShowDecade(date) {
+  switch (date.getFullYear()) {
+    case 2000:
+      return false;
+    case 2100:
+      return {
+        content: '💯',
+        classes: 'background-success',
+      };
+  }
+},
+      };
+      window.addError = (el, message) => {
+        el.classList.add('is-invalid-input');
+        el.parentElement.querySelector('.form-error').textContent = message;
+      };
+      window.removeErrors = (el) => {
+        el.classList.remove('is-invalid-input');
+        el.parentElement.querySelector('.form-error').textContent = '';
+      };
+
+      $(document).foundation();
+      initialize();
+
+      document.getElementById('types').querySelectorAll('input').forEach((el) => {
+        el.addEventListener('change', onChangeType);
+      });
+
+      document.getElementById('direction').querySelectorAll('input').forEach((el) => {
+        el.addEventListener('change', onChnageDirection);
+      });
+
+      const optsForm = document.getElementById('options');
+      optsForm.querySelectorAll('input[type=text], input[type=radio], select').forEach((el) => {
+        el.addEventListener('change', onChangeInputOption);
+      });
+      optsForm.querySelectorAll('textarea').forEach((el) => {
+        let listener;
+        if (el.dataset.validator) {
+          el.addEventListener('change', (ev) => {
+            if (el.classList.contains(Foundation.Abide.defaults.inputErrorClass)) {
+              return;
+            }
+            onChangeTextareaOption(ev);
+          });
+        } else {
+          listener = onChangeTextareaOption;
+        }
+        el.addEventListener('change', listener);
+      });
+      optsForm.querySelectorAll('input[type=checkbox]').forEach((el) => {
+        el.addEventListener('click', onClickCheckboxOptions);
+      });
+
+      switchPicker('input');
+
+      const initTooltips = () => {
+        document.querySelectorAll('[data-tooltip]').forEach((el) => {
+          if (!el.classList.contains('has-tip')) {
+            new Foundation.Tooltip($(el));
+          }
+        });
+      };
+      setInterval(initTooltips, 1000);
+    </script>
+  </body>
+</html>

+ 547 - 0
View/vanillajs-datepicker/demo/index.html

@@ -0,0 +1,547 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <title>Vanilla JS Datepicker Demo</title>
+    <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/bulma@0.8.0/css/bulma.css">
+    <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/bulma-tooltip@3.0.2/dist/css/bulma-tooltip.min.css">
+    <link rel="stylesheet" type="text/css" href="../dist/css/datepicker-bulma.css">
+    <script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
+    <style type="text/css">
+      aside {
+        position: fixed;
+        top: 0;
+        bottom: 0;
+        right: -300px;
+        width: 300px;
+        overflow: auto;
+        background-color: #fff;
+        box-shadow: inset 1px 1px rgba(0, 0, 0, 10%);
+        transition: right 0.3s;
+      }
+      .open aside {
+        right: 0;
+      }
+      aside hr {
+        margin-top: 0.5rem;
+      }
+      .code-wrap {
+        position: relative;
+      }
+      .code-wrap pre:not(.is-active) {
+        height: 0;
+        overflow: hidden;
+        opacity: 0.5;
+      }
+      .code-wrap .collapse-button {
+        position: absolute;
+        top: 0;
+        right: 0;
+        left: auto;
+        cursor: pointer;
+        padding: 0.125rem 0.25rem;
+        font-size: 0.75rem;
+      }
+
+      .toggle-btn {
+        position: fixed;
+        top: 0.75rem;
+        right: 0.75rem;
+        width: 1.5rem;
+        background-color: #fff;
+        line-height: 1.5rem;
+        border: 1px solid rgba(0, 0, 0, 10%);
+        border-radius: 2px;
+        box-shadow: 1px 1px rgba(0, 0, 0, 10%);
+        cursor: pointer;
+      }
+      .toggle-btn::before {
+        content: '\25c0';
+        padding-left: 0.25rem;
+      }
+      .open .toggle-btn::before {
+        content: '\25b6';
+      }
+
+      @media (min-width: 481px) {
+        main {
+          margin-right: 38.1966%;
+        }
+        aside {
+          right: 0;
+          width: 38.1966%;
+        }
+        .toggle-btn {
+          display: none;
+        }
+      }
+    </style>
+  </head>
+  <body>
+    <main>
+      <section class="section container is-fluid">
+        <div class="columns">
+          <div class="column">
+            <p>Vanilla JS Datepicker</p>
+            <h1 class="title">Demo</h1>
+            <div id="sandbox"></div>
+          </div>
+        </div>
+        <div class="columns">
+          <div class="column content">
+            <p>Style: <em>Bulma</em> | <a href="bs4.html">Bootstrap</a> | <a href="foundation.html">Foundation</a> | <a href="plain-css.html">Plain CSS</a></p>
+            <p class="is-size-7">* This page uses <a href="https://wikiki.github.io/elements/tooltip/" target="_blank">bulma-tooltip</a> for tooltips.</p>
+          </div>
+        </div>
+      </section>
+    </main>
+
+    <aside>
+      <section class="section">
+        <div class="tile is-parent is-vertical">
+          <div class="tile is-child">
+            <h4 class="subtitle">Type</h4>
+            <form id="types">
+              <div class="field">
+                <div class="control">
+                  <label class="radio">
+                    <input type="radio" name="type" value="input" checked>
+                    Input
+                  </label>
+                  <label class="radio">
+                    <input type="radio" name="type" value="inline">
+                    Inline
+                  </label>
+                  <label class="radio">
+                    <input type="radio" name="type" value="range">
+                    Range
+                  </label>
+                </div>
+              </div>
+            </form>
+          </div>
+          <hr>
+          <div class="tile is-child">
+            <h4 class="subtitle">Options</h4>
+            <form id="options">
+
+              <div class="field">
+                <div class="control tooltip" data-tooltip="Only effective in range picker">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="allowOneSidedRange" value="true">
+                    allowOneSidedRange
+                  </label>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="autohide" value="true">
+                    autohide
+                  </label>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="beforeShowDay" value="true">
+                    beforeShowDay
+                  </label>
+                </div>
+                <div class="code-wrap">
+                  <pre id="code-beforeShowDay"></pre>
+                  <div class="collapse-button" data-target="code-beforeShowDay">show/hide</div>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="beforeShowMonth" value="true">
+                    beforeShowMonth
+                  </label>
+                </div>
+                <div class="code-wrap">
+                  <pre id="code-beforeShowMonth"></pre>
+                  <div class="collapse-button" data-target="code-beforeShowMonth">show/hide</div>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="beforeShowYear" value="true">
+                    beforeShowYear
+                  </label>
+                </div>
+                <div class="code-wrap">
+                  <pre id="code-beforeShowYear"></pre>
+                  <div class="collapse-button" data-target="code-beforeShowYear">show/hide</div>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="beforeShowDecade" value="true">
+                    beforeShowDecade
+                  </label>
+                </div>
+                <div class="code-wrap">
+                  <pre id="code-beforeShowDecade"></pre>
+                  <div class="collapse-button" data-target="code-beforeShowDecade">show/hide</div>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="calendarWeeks" value="true">
+                    calendarWeeks
+                  </label>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="clearBtn" value="true">
+                    clearBtn
+                  </label>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">dateDelimiter</label>
+                <div class="control">
+                  <input type="text" class="input" name="dateDelimiter" placeholder=",">
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">datesDisabled</label>
+                <div class="control tooltip" data-tooltip="enter in JSON format">
+                  <textarea class="input" name="datesDisabled" placeholder="[]"></textarea>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">daysOfWeekDisabled</label>
+                <div class="control">
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekDisabled" value="0"> 0
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekDisabled" value="1"> 1
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekDisabled" value="2"> 2
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekDisabled" value="3"> 3
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekDisabled" value="4"> 4
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekDisabled" value="5"> 5
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekDisabled" value="6"> 6
+                  </label>&nbsp;
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">daysOfWeekHighlighted</label>
+                <div class="control">
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekHighlighted" value="0"> 0
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekHighlighted" value="1"> 1
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekHighlighted" value="2"> 2
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekHighlighted" value="3"> 3
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekHighlighted" value="4"> 4
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekHighlighted" value="5"> 5
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekHighlighted" value="6"> 6
+                  </label>&nbsp;
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">defaultViewDate</label>
+                <div class="control">
+                  <input type="text" class="input" name="defaultViewDate" placeholder="today">
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="disableTouchKeyboard" value="true">
+                    disableTouchKeyboard
+                  </label>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">format</label>
+                <div class="control">
+                  <input type="text" class="input" name="format" placeholder="mm/dd/yyyy">
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">language</label>
+                <div class="control">
+                  <div class="select">
+                    <select name="language" >
+                      <option value="en">en – English (US)</option>
+                    </select>
+                  </div>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">maxDate</label>
+                <div class="control">
+                  <input type="text" class="input" name="maxDate" placeholder="null">
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">maxNumberOfDates</label>
+                <div class="control tooltip" data-tooltip="Not effective in range picker">
+                  <input type="text" class="input" name="maxNumberOfDates" placeholder="1">
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">maxView</label>
+                <div class="control">
+                  <div class="select">
+                    <select name="maxView">
+                      <option value="0">0 – days</option>
+                      <option value="1">1 – months</option>
+                      <option value="2">2 – years</option>
+                      <option value="3" selected>3 – decades</option>
+                    </select>
+                  </div>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">minDate</label>
+                <div class="control">
+                  <input type="text" class="input" name="minDate" placeholder="null">
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">nextArrow</label>
+                <div class="control">
+                  <textarea class="input" name="nextArrow" placeholder="»"></textarea>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">orientation</label>
+                <div class="control">
+                  <div class="select">
+                    <select name="orientation" >
+                      <option value="auto">auto</option>
+                      <option value="top auto">top auto</option>
+                      <option value="bottom auto">bottom auto</option>
+                      <option value="auto left">auto left</option>
+                      <option value="top left">top left</option>
+                      <option value="bottom left">bottom left</option>
+                      <option value="auto right">auto right</option>
+                      <option value="top right">top right</option>
+                      <option value="bottom right">bottom right</option>
+                    </select>
+                  </div>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">pickLevel</label>
+                <div class="control">
+                  <div class="select">
+                    <select name="pickLevel">
+                      <option value="0">0 – date</option>
+                      <option value="1">1 – month</option>
+                      <option value="2">2 – year</option>
+                    </select>
+                  </div>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">prevArrow</label>
+                <div class="control">
+                  <textarea class="input" name="prevArrow" placeholder="«"></textarea>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="showDaysOfWeek" value="true" checked>
+                    showDaysOfWeek
+                  </label>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="showOnClick" value="true" checked>
+                    showOnClick
+                  </label>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="showOnFocus" value="true" checked>
+                    showOnFocus
+                  </label>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">startView</label>
+                <div class="control">
+                  <div class="select">
+                    <select name="startView">
+                      <option value="0">0 – days</option>
+                      <option value="1">1 – months</option>
+                      <option value="2">2 – years</option>
+                      <option value="3">3 – decades</option>
+                    </select>
+                  </div>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">title</label>
+                <div class="control">
+                  <input type="text" class="input" name="title">
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="todayBtn" value="true">
+                    todayBtn
+                  </label>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">todayBtnMode</label>
+                <div class="control">
+                  <div class="select">
+                    <select name="todayBtnMode">
+                      <option value="0">0 – focus</option>
+                      <option value="1">1 – select</option>
+                    </select>
+                  </div>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="todayHighlight" value="true">
+                    todayHighlight
+                  </label>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="updateOnBlur" value="true" checked>
+                    updateOnBlur
+                  </label>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">weekStart</label>
+                <div class="control">
+                  <input type="text" class="input" name="weekStart" placeholder="0">
+                </div>
+              </div>
+
+            </form>
+          </div>
+          <hr>
+          <div class="tile is-child">
+            <h4 class="subtitle">Text direction</h4>
+            <form id="direction">
+              <div class="field">
+                <div class="control">
+                  <label class="radio">
+                    <input type="radio" name="direction" value="ltr" checked>
+                    LTR
+                  </label>
+                  <label class="radio">
+                    <input type="radio" name="direction" value="rtl">
+                    RTL
+                  </label>
+                </div>
+              </div>
+            </form>
+          </div>
+        </div>
+      </section>
+    </aside>
+
+    <div class="toggle-btn"></div>
+    <script src="../dist/js/datepicker-full.js"></script>
+    <script src="./live-demo.js"></script>
+    <script>
+      /*global initialize onChangeType onChnageDirection onChangeInputOption onChangeInputOption onChangeTextareaOption onClickCheckboxOptions switchPicker */
+      initialize();
+
+      document.getElementById('types').querySelectorAll('input').forEach((el) => {
+        el.addEventListener('change', onChangeType);
+      });
+
+      document.getElementById('direction').querySelectorAll('input').forEach((el) => {
+        el.addEventListener('change', onChnageDirection);
+      });
+
+      const optsForm = document.getElementById('options');
+      optsForm.querySelectorAll('input[type=text], input[type=radio], select').forEach((el) => {
+        el.addEventListener('change', onChangeInputOption);
+      });
+      optsForm.querySelectorAll('textarea').forEach((el) => {
+        el.addEventListener('change', onChangeTextareaOption);
+      });
+      optsForm.querySelectorAll('.checkbox').forEach((el) => {
+        el.addEventListener('click', onClickCheckboxOptions);
+      });
+
+      switchPicker('input');
+    </script>
+  </body>
+</html>

+ 469 - 0
View/vanillajs-datepicker/demo/live-demo.js

@@ -0,0 +1,469 @@
+/*eslint no-unused-vars: ["error", { "varsIgnorePattern": "initialize|on[A-Z]*" }]*/
+var templates = {
+  input: `<div class="field">
+  <div class="control">
+    <input type="text" class="input date">
+  </div>
+</div>`,
+  inline: `<div class="date"></div>`,
+  range: `<div class="field has-addons date">
+  <div class="control">
+    <input type="text" name="range-start" class="input">
+  </div>
+  <div class="control">
+    <a class="button is-static">to</a>
+  </div>
+  <div class="control">
+    <input type="text" name="range-end" class="input">
+  </div>
+</div>`,
+};
+var beforeShowFns = {
+beforeShowDay(date) {
+  if (date.getMonth() == new Date().getMonth()) {
+    switch (date.getDate()) {
+      case 4:
+        return {
+          content: '<span class="tooltip" data-tooltip="Example tooltip">4</span>',
+          classes: 'has-background-info'
+        };
+      case 8:
+        return false;
+      case 12:
+        return "has-text-success";
+    }
+  }
+},
+beforeShowMonth(date) {
+  switch (date.getMonth()) {
+    case 6:
+      if (date.getFullYear() === new Date().getFullYear()) {
+        return {content: '🎉'};
+      }
+      break;
+    case 8:
+      return false;
+  }
+},
+beforeShowYear(date) {
+  switch (date.getFullYear()) {
+    case 2017:
+      return false;
+    case 2020:
+      return {content: '<span class="tooltip is-tooltip-bottom" data-tooltip="Tooltip text">2020</span>'};
+  }
+},
+beforeShowDecade(date) {
+  switch (date.getFullYear()) {
+    case 2000:
+      return false;
+    case 2100:
+      return {
+        content: '💯',
+        classes: 'is-background-success',
+      };
+  }
+},
+};
+var buttonClass;
+
+const today = new Date().setHours(0, 0, 0, 0);
+const defaultOptions = {
+  allowOneSidedRange: false,
+  autohide: false,
+  beforeShowDay: null,
+  beforeShowDecade: null,
+  beforeShowMonth: null,
+  beforeShowYear: null,
+  calendarWeeks: false,
+  clearBtn: false,
+  dateDelimiter: ',',
+  datesDisabled: [],
+  daysOfWeekDisabled: [],
+  daysOfWeekHighlighted: [],
+  defaultViewDate: today,
+  disableTouchKeyboard: false,
+  format: 'mm/dd/yyyy',
+  language: 'en',
+  maxDate: null,
+  maxNumberOfDates: 1,
+  maxView: 3,
+  minDate: null,
+  nextArrow: '»',
+  orientation: 'auto',
+  pickLevel: 0,
+  prevArrow: '«',
+  showDaysOfWeek: true,
+  showOnClick: true,
+  showOnFocus: true,
+  startView: 0,
+  title: '',
+  todayBtn: false,
+  todayHighlight: false,
+  updateOnBlur: true,
+  weekStart: 0,
+};
+const languages = {
+  'ar-tn': 'Arabic-Tunisia',
+  ar: 'Arabic',
+  az: 'Azerbaijani',
+  bg: 'Bulgarian',
+  bm: 'Bamanankan',
+  bn: 'Bengali (Bangla)',
+  br: 'Breton',
+  bs: 'Bosnian',
+  ca: 'Catalan',
+  cs: 'Czech',
+  cy: 'Welsh',
+  da: 'Danish',
+  de: 'German',
+  el: 'Greek',
+  'en-AU': 'Australian English',
+  'en-CA': 'Canadian English',
+  'en-GB': 'British English',
+  'en-IE': 'Irish English',
+  'en-NZ': 'New Zealand English',
+  'en-ZA': 'South African English',
+  eo: 'Esperanto',
+  es: 'Spanish',
+  et: 'Estonian',
+  eu: 'Basque',
+  fa: 'Persian',
+  fi: 'Finnish',
+  fo: 'Faroese',
+  'fr-CH': 'French (Switzerland)',
+  fr: 'French',
+  gl: 'Galician',
+  he: 'Hebrew',
+  hi: 'Hindi',
+  hr: 'Croatian',
+  hu: 'Hungarian',
+  hy: 'Armenian',
+  id: 'Bahasa',
+  is: 'Icelandic',
+  'it-CH': 'Italian (Switzerland)',
+  it: 'Italian',
+  ja: 'Japanese',
+  ka: 'Georgian',
+  kk: 'Kazakh',
+  km: 'Khmer',
+  ko: 'Korean',
+  lt: 'Lithuanian',
+  lv: 'Latvian',
+  me: 'Montenegrin',
+  mk: 'Macedonian',
+  mn: 'Mongolian',
+  mr: 'Marathi',
+  ms: 'Malay',
+  'nl-BE': 'Belgium-Dutch',
+  nl: 'Dutch',
+  no: 'Norwegian',
+  oc: 'Occitan',
+  pl: 'Polish',
+  'pt-BR': 'Brazilian',
+  pt: 'Portuguese',
+  ro: 'Romanian',
+  ru: 'Russian',
+  si: 'Sinhala',
+  sk: 'Slovak',
+  sl: 'Slovene',
+  sq: 'Albanian',
+  'sr-latn': 'Serbian latin',
+  sr: 'Serbian cyrillic',
+  sv: 'Swedish',
+  sw: 'Swahili',
+  ta: 'Tamil',
+  tg: 'Tajik',
+  th: 'Thai',
+  tk: 'Turkmen',
+  tr: 'Turkish',
+  uk: 'Ukrainian',
+  'uz-cyrl': 'Uzbek cyrillic',
+  'uz-latn': 'Uzbek latin',
+  vi: 'Vietnamese',
+  'zh-CN': 'Simplified Chinese',
+  'zh-TW': 'Traditional Chinese',
+};
+const range = document.createRange();
+const sandbox = document.getElementById('sandbox');
+const options = document.getElementById('options');
+const jsonFields = ['datesDisabled'];
+
+function parseHTML(html) {
+  return range.createContextualFragment(html);
+}
+
+function getBeforeShowFnSrc(name) {
+  return beforeShowFns[name].toString();
+}
+
+function switchPicker(type) {
+  const options = buttonClass ? {buttonClass} : {};
+  if (window.demoPicker) {
+    const currentOpts = window.demoPicker instanceof DateRangePicker
+      ? window.demoPicker.datepickers[0]._options
+      : window.demoPicker._options;
+    Object.keys(defaultOptions).reduce((opts, key) => {
+      if (key in currentOpts && String(currentOpts[key] !== String(defaultOptions[key]))) {
+        opts[key] = currentOpts[key];
+      }
+      return opts;
+    }, options);
+
+    window.demoPicker.destroy();
+    sandbox.removeChild(sandbox.firstChild);
+  }
+  sandbox.appendChild(parseHTML(templates[type]));
+
+  const el = sandbox.querySelector('.date');
+  window.demoPicker = type === 'range'
+    ? new DateRangePicker(el, options)
+    : new Datepicker(el, options);
+}
+
+const setOptions = function setOptions(name, value) {
+  window.demoPicker.setOptions({[name]: value});
+  refreshOptionForm();
+};
+const refreshOptionForm = function refreshOptionForm() {
+  const demoPicker = window.demoPicker;
+  const rangePicker = demoPicker instanceof DateRangePicker;
+  const datepicker = rangePicker ? demoPicker.datepickers[0] : demoPicker;
+  const optsForm = document.getElementById('options');
+  const {config, _options} = datepicker;
+  const configDefaults = {
+    minDate: new Date(today).setFullYear(0, 0, 1),
+    maxDate: undefined,
+  };
+  const formElemByName = name => optsForm.querySelector(`[name="${name}"]`);
+  const formatDate = val => Datepicker.formatDate(val, config.format, config.lang);
+
+  if (!rangePicker) {
+    const allowOneSided = formElemByName('allowOneSidedRange');
+    if (allowOneSided.checked) {
+      allowOneSided.checked = false;
+    }
+  }
+  Object.entries(datepicker.config).forEach(entry => {
+    const [key, val] = entry;
+    let el;
+    switch (key) {
+      case 'format':
+      case 'weekStart':
+        el = formElemByName(key);
+        if (el.value || val !== defaultOptions[key]) {
+          el.value = val;
+        }
+        break;
+      case 'minDate':
+      case 'maxDate':
+        el = formElemByName(key);
+        if (val === configDefaults[key]) {
+          if (!el.value || el.value === 'null') {
+            break;
+          }
+          if (_options[key] === null) {
+            el.value = '';
+            break;
+          }
+        }
+        el.value = formatDate(val);
+        break;
+      case 'datesDisabled':
+        el = formElemByName(key);
+        if (val.length === 0) {
+          if (!el.value || el.value === '[]') {
+            break;
+          }
+          if (String(_options.datesDisabled) === '[]') {
+            el.value = '';
+            break;
+          }
+        }
+        el.value = JSON.stringify(val.map(item => formatDate(item)));
+        break;
+      case 'daysOfWeekDisabled':
+      case 'daysOfWeekHighlighted':
+        optsForm.querySelectorAll(`[name=${key}`).forEach(chkbox => {
+          chkbox.checked = val.includes(Number.parseInt(chkbox.value, 10));
+        });
+        break;
+      case 'defaultViewDate':
+        el = formElemByName(key);
+        if (val === defaultOptions[key]) {
+          if (!el.value || el.value === 'today') {
+            break;
+          }
+          if (_options[key] === 'today') {
+            el.value = '';
+            break;
+          }
+        }
+        el.value = formatDate(val);
+        break;
+      case 'maxView':
+      case 'pickLevel':
+      case 'startView':
+        formElemByName(key).value = val;
+        break;
+      case 'maxNumberOfDates':
+        el = formElemByName(key);
+        if (rangePicker) {
+          if (el.value) {
+            el.value = '';
+          }
+          break;
+        }
+        if (el.value || val !== defaultOptions[key]) {
+          el.value = val;
+        }
+        break;
+    }
+  });
+};
+const handleArrayOption = function handleArrayOption(name) {
+  const checkedInputs = options.querySelectorAll(`input[name=${name}]:checked`);
+  setOptions(name, Array.from(checkedInputs).map(el => Number(el.value)));
+};
+
+function updateOption(name, value) {
+  switch (name) {
+    case 'beforeShowDay':
+    case 'beforeShowMonth':
+    case 'beforeShowYear':
+    case 'beforeShowDecade':
+      setOptions(name, value ? beforeShowFns[name] : null);
+      return;
+    case 'daysOfWeekDisabled':
+    case 'daysOfWeekHighlighted':
+      handleArrayOption(name, value);
+      return;
+  }
+
+  let newValue;
+  if (typeof value === 'string') {
+    switch (value) {
+      case '':
+        newValue = defaultOptions[name];
+        break;
+      case 'null':
+        newValue = null;
+        break;
+      case 'false':
+        newValue = false;
+        break;
+      case 'true':
+        newValue = true;
+        break;
+      default:
+        newValue = Number(value);
+        if (isNaN(newValue)) {
+          newValue = value;
+        }
+    }
+  } else {
+    newValue = value;
+  }
+  setOptions(name, newValue);
+}
+
+function addError(el, message) {
+  const field = el.parentElement.parentElement;
+  field.appendChild(parseHTML(`<p class="help is-danger">${message}</p>`));
+  el.classList.add('is-danger');
+}
+
+function removeErrors(el) {
+  const field = el.parentElement.parentElement;
+  field.querySelectorAll('.help.is-danger').forEach((errMsg) => {
+    field.removeChild(errMsg);
+  });
+  el.classList.remove('is-danger');
+}
+
+function onChangeType(ev) {
+  switchPicker(ev.target.value);
+  refreshOptionForm();
+}
+
+function onChnageDirection(ev) {
+  const defaultDir = window.getComputedStyle(document.body).direction;
+  const dir = ev.target.value;
+  const mainElem = document.querySelector('main');
+
+  if (dir !== defaultDir) {
+    mainElem.dir = dir;
+  } else {
+    mainElem.removeAttribute('dir');
+  }
+}
+
+function onChangeInputOption(ev) {
+  updateOption(ev.target.name, ev.target.value);
+}
+
+function onChangeTextareaOption(ev) {
+  let {value, name} = ev.target;
+  if (jsonFields.includes(name)) {
+    removeErrors(ev.target);
+    if (value.length > 0) {
+      try {
+        value = JSON.parse(value);
+      } catch (err) {
+        addError(ev.target, 'Invalid JSON string');
+        return;
+      }
+    }
+  }
+  if (name === 'datesDisabled') {
+    if (value && !Array.isArray(value)) {
+      addError(ev.target, 'This option must be an array');
+      return;
+    }
+  }
+  updateOption(name, value);
+}
+
+function onClickCheckboxOptions(ev) {
+  ev.stopPropagation();
+
+  let checkbox;
+  let checked;
+  if (ev.target.tagName === 'INPUT') {
+    checkbox = ev.target;
+    checked = checkbox.checked;
+  } else {
+    ev.preventDefault();
+    checkbox = ev.currentTarget.querySelector('input');
+    checked = checkbox.checked = !checkbox.checked;
+  }
+
+  const value = checkbox.value === 'true' ? checked : checkbox.value;
+  updateOption(checkbox.name, value);
+}
+
+function initialize() {
+  // load languages
+  const selectElem = options.querySelector('select[name=language]');
+  Object.keys(languages).forEach((lang) => {
+    document.body.appendChild(parseHTML(`<script src="../dist/js/locales/${lang}.js"></script>`));
+    selectElem.appendChild(parseHTML(`<option value="${lang}">${lang} – ${languages[lang]}</option>`));
+  });
+
+  document.querySelector('.toggle-btn').addEventListener('click', () => {
+    document.body.classList.toggle('open');
+  });
+
+  document.querySelectorAll('.code-wrap pre').forEach((el) => {
+    el.textContent = getBeforeShowFnSrc(el.id.replace('code-', ''));
+  });
+
+  // collapsibles
+  document.querySelectorAll('.collapse-button').forEach((el) => {
+    el.addEventListener('click', () => {
+      const target = document.getElementById(el.dataset.target);
+      el.classList.toggle('is-active');
+      target.classList.toggle('is-active');
+    });
+  });
+}

+ 584 - 0
View/vanillajs-datepicker/demo/plain-css.html

@@ -0,0 +1,584 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <title>Vanilla JS Datepicker Demo</title>
+    <link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/bulma-tooltip@3.0.2/dist/css/bulma-tooltip.min.css">
+    <link rel="stylesheet" type="text/css" href="../dist/css/datepicker.css">
+    <script defer src="https://use.fontawesome.com/releases/v5.3.1/js/all.js"></script>
+    <style type="text/css">
+      pre {
+        background-color: #f5f5f5;
+        padding: 1rem;
+        overflow-x: auto;
+      }
+      .section {
+        padding: 1.5rem;
+      }
+      .field {
+        margin-bottom: 1em;
+      }
+      .has-addons {
+        display: flex;
+      }
+      .has-addons .control a {
+        padding-left: 0.5rem;
+        padding-right: 0.5rem;
+      }
+      .label {
+        font-weight: 600;
+      }
+
+      aside {
+        position: fixed;
+        top: 0;
+        bottom: 0;
+        right: -300px;
+        width: 300px;
+        overflow: auto;
+        background-color: #fff;
+        box-shadow: inset 1px 1px rgba(0, 0, 0, 10%);
+        transition: right 0.3s;
+      }
+      .open aside {
+        right: 0;
+      }
+      aside hr {
+        margin-top: 0.5rem;
+      }
+      .code-wrap {
+        position: relative;
+      }
+      .code-wrap pre:not(.is-active) {
+        height: 0;
+        overflow: hidden;
+        opacity: 0.5;
+      }
+      .code-wrap .collapse-button {
+        position: absolute;
+        top: 0;
+        right: 0;
+        left: auto;
+        cursor: pointer;
+        padding: 0.125rem 0.25rem;
+        font-size: 0.75rem;
+      }
+      label.checkbox {
+        display: inline-block;
+      }
+      .help {
+        margin: 0;
+      }
+      .help.is-danger {
+        color: #f14668;
+      }
+
+      .toggle-btn {
+        position: fixed;
+        top: 0.75rem;
+        right: 0.75rem;
+        width: 1.5rem;
+        background-color: #fff;
+        line-height: 1.5rem;
+        border: 1px solid rgba(0, 0, 0, 10%);
+        border-radius: 2px;
+        box-shadow: 1px 1px rgba(0, 0, 0, 10%);
+        cursor: pointer;
+      }
+      .toggle-btn::before {
+        content: '\25c0';
+        padding-left: 0.25rem;
+      }
+      .open .toggle-btn::before {
+        content: '\25b6';
+      }
+
+      .has-background-info {
+        background-color: cyan;
+      }
+      .has-text-success {
+        color: green;
+      }
+
+      @media (min-width: 481px) {
+        main {
+          margin-right: 38.1966%;
+        }
+        aside {
+          right: 0;
+          width: 38.1966%;
+        }
+        .toggle-btn {
+          display: none;
+        }
+      }
+    </style>
+  </head>
+  <body>
+    <main>
+      <section class="section container is-fluid">
+        <div class="columns">
+          <div class="column">
+            <p>Vanilla JS Datepicker</p>
+            <h1 class="title">Demo</h1>
+            <div id="sandbox"></div>
+          </div>
+        </div>
+        <div class="columns">
+          <div class="column">
+            <p><small>Style: <a href="index.html">Bulma</a> | <a href="bs4.html">Bootstrap</a> | <a href="foundation.html">Foundation</a> | <em>Plain CSS</em></small></p>
+            <p><small>* This page uses <a href="https://wikiki.github.io/elements/tooltip/" target="_blank">bulma-tooltip</a> for tooltips.</small></p>
+          </div>
+        </div>
+      </section>
+    </main>
+
+    <aside>
+      <section class="section">
+        <div class="tile is-parent is-vertical">
+          <div class="tile is-child">
+            <h4 class="subtitle">Type</h4>
+            <form id="types">
+              <div class="field">
+                <div class="control">
+                  <label class="radio">
+                    <input type="radio" name="type" value="input" checked>
+                    Input
+                  </label>
+                  <label class="radio">
+                    <input type="radio" name="type" value="inline">
+                    Inline
+                  </label>
+                  <label class="radio">
+                    <input type="radio" name="type" value="range">
+                    Range
+                  </label>
+                </div>
+              </div>
+            </form>
+          </div>
+          <hr>
+          <div class="tile is-child">
+            <h4 class="subtitle">Options</h4>
+            <form id="options">
+
+              <div class="field">
+                <div class="control tooltip" data-tooltip="Only effective in range picker">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="allowOneSidedRange" value="true">
+                    allowOneSidedRange
+                  </label>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="autohide" value="true">
+                    autohide
+                  </label>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="beforeShowDay" value="true">
+                    beforeShowDay
+                  </label>
+                </div>
+                <div class="code-wrap">
+                  <pre id="code-beforeShowDay"></pre>
+                  <div class="collapse-button" data-target="code-beforeShowDay">show/hide</div>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="beforeShowMonth" value="true">
+                    beforeShowMonth
+                  </label>
+                </div>
+                <div class="code-wrap">
+                  <pre id="code-beforeShowMonth"></pre>
+                  <div class="collapse-button" data-target="code-beforeShowMonth">show/hide</div>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="beforeShowYear" value="true">
+                    beforeShowYear
+                  </label>
+                </div>
+                <div class="code-wrap">
+                  <pre id="code-beforeShowYear"></pre>
+                  <div class="collapse-button" data-target="code-beforeShowYear">show/hide</div>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="beforeShowDecade" value="true">
+                    beforeShowDecade
+                  </label>
+                </div>
+                <div class="code-wrap">
+                  <pre id="code-beforeShowDecade"></pre>
+                  <div class="collapse-button" data-target="code-beforeShowDecade">show/hide</div>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="calendarWeeks" value="true">
+                    calendarWeeks
+                  </label>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="clearBtn" value="true">
+                    clearBtn
+                  </label>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">dateDelimiter</label>
+                <div class="control">
+                  <input type="text" class="input" name="dateDelimiter" placeholder=",">
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">datesDisabled</label>
+                <div class="control tooltip" data-tooltip="enter in JSON format">
+                  <textarea class="input" name="datesDisabled" placeholder="[]"></textarea>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">daysOfWeekDisabled</label>
+                <div class="control">
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekDisabled" value="0"> 0
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekDisabled" value="1"> 1
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekDisabled" value="2"> 2
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekDisabled" value="3"> 3
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekDisabled" value="4"> 4
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekDisabled" value="5"> 5
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekDisabled" value="6"> 6
+                  </label>&nbsp;
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">daysOfWeekHighlighted</label>
+                <div class="control">
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekHighlighted" value="0"> 0
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekHighlighted" value="1"> 1
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekHighlighted" value="2"> 2
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekHighlighted" value="3"> 3
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekHighlighted" value="4"> 4
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekHighlighted" value="5"> 5
+                  </label>&nbsp;
+                  <label class="checkbox">
+                    <input type="checkbox" name="daysOfWeekHighlighted" value="6"> 6
+                  </label>&nbsp;
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">defaultViewDate</label>
+                <div class="control">
+                  <input type="text" class="input" name="defaultViewDate" placeholder="today">
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="disableTouchKeyboard" value="true">
+                    disableTouchKeyboard
+                  </label>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">format</label>
+                <div class="control">
+                  <input type="text" class="input" name="format" placeholder="mm/dd/yyyy">
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">language</label>
+                <div class="control">
+                  <div class="select">
+                    <select name="language" >
+                      <option value="en">en – English (US)</option>
+                    </select>
+                  </div>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">maxDate</label>
+                <div class="control">
+                  <input type="text" class="input" name="maxDate" placeholder="null">
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">maxNumberOfDates</label>
+                <div class="control tooltip" data-tooltip="Not effective in range picker">
+                  <input type="text" class="input" name="maxNumberOfDates" placeholder="1">
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">maxView</label>
+                <div class="control">
+                  <div class="select">
+                    <select name="maxView">
+                      <option value="0">0 – days</option>
+                      <option value="1">1 – months</option>
+                      <option value="2">2 – years</option>
+                      <option value="3" selected>3 – decades</option>
+                    </select>
+                  </div>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">minDate</label>
+                <div class="control">
+                  <input type="text" class="input" name="minDate" placeholder="null">
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">nextArrow</label>
+                <div class="control">
+                  <textarea class="input" name="nextArrow" placeholder="»"></textarea>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">orientation</label>
+                <div class="control">
+                  <div class="select">
+                    <select name="orientation" >
+                      <option value="auto">auto</option>
+                      <option value="top auto">top auto</option>
+                      <option value="bottom auto">bottom auto</option>
+                      <option value="auto left">auto left</option>
+                      <option value="top left">top left</option>
+                      <option value="bottom left">bottom left</option>
+                      <option value="auto right">auto right</option>
+                      <option value="top right">top right</option>
+                      <option value="bottom right">bottom right</option>
+                    </select>
+                  </div>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">pickLevel</label>
+                <div class="control">
+                  <div class="select">
+                    <select name="pickLevel">
+                      <option value="0">0 – date</option>
+                      <option value="1">1 – month</option>
+                      <option value="2">2 – year</option>
+                    </select>
+                  </div>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">prevArrow</label>
+                <div class="control">
+                  <textarea class="input" name="prevArrow" placeholder="«"></textarea>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="showDaysOfWeek" value="true" checked>
+                    showDaysOfWeek
+                  </label>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="showOnClick" value="true" checked>
+                    showOnClick
+                  </label>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="showOnFocus" value="true" checked>
+                    showOnFocus
+                  </label>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">startView</label>
+                <div class="control">
+                  <div class="select">
+                    <select name="startView">
+                      <option value="0">0 – days</option>
+                      <option value="1">1 – months</option>
+                      <option value="2">2 – years</option>
+                      <option value="3">3 – decades</option>
+                    </select>
+                  </div>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">title</label>
+                <div class="control">
+                  <input type="text" class="input" name="title">
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="todayBtn" value="true">
+                    todayBtn
+                  </label>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">todayBtnMode</label>
+                <div class="control">
+                  <div class="select">
+                    <select name="todayBtnMode">
+                      <option value="0">0 – focus</option>
+                      <option value="1">1 – select</option>
+                    </select>
+                  </div>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="todayHighlight" value="true">
+                    todayHighlight
+                  </label>
+                </div>
+              </div>
+
+              <div class="field">
+                <div class="control">
+                  <label class="label checkbox">
+                    <input type="checkbox" name="updateOnBlur" value="true" checked>
+                    updateOnBlur
+                  </label>
+                </div>
+              </div>
+
+              <div class="field">
+                <label class="label">weekStart</label>
+                <div class="control">
+                  <input type="text" class="input" name="weekStart" placeholder="0">
+                </div>
+              </div>
+
+            </form>
+          </div>
+          <hr>
+          <div class="tile is-child">
+            <h4 class="subtitle">Text direction</h4>
+            <form id="direction">
+              <div class="field">
+                <div class="control">
+                  <label class="radio">
+                    <input type="radio" name="direction" value="ltr" checked>
+                    LTR
+                  </label>
+                  <label class="radio">
+                    <input type="radio" name="direction" value="rtl">
+                    RTL
+                  </label>
+                </div>
+              </div>
+            </form>
+          </div>
+        </div>
+      </section>
+    </aside>
+
+    <div class="toggle-btn"></div>
+    <script src="../dist/js/datepicker-full.js"></script>
+    <script src="./live-demo.js"></script>
+    <script>
+      /*global initialize onChangeType onChnageDirection onChangeInputOption onChangeInputOption onChangeTextareaOption onClickCheckboxOptions switchPicker */
+      initialize();
+
+      document.getElementById('types').querySelectorAll('input').forEach((el) => {
+        el.addEventListener('change', onChangeType);
+      });
+
+      document.getElementById('direction').querySelectorAll('input').forEach((el) => {
+        el.addEventListener('change', onChnageDirection);
+      });
+
+      const optsForm = document.getElementById('options');
+      optsForm.querySelectorAll('input[type=text], input[type=radio], select').forEach((el) => {
+        el.addEventListener('change', onChangeInputOption);
+      });
+      optsForm.querySelectorAll('textarea').forEach((el) => {
+        el.addEventListener('change', onChangeTextareaOption);
+      });
+      optsForm.querySelectorAll('.checkbox').forEach((el) => {
+        el.addEventListener('click', onClickCheckboxOptions);
+      });
+
+      switchPicker('input');
+    </script>
+  </body>
+</html>

+ 276 - 0
View/vanillajs-datepicker/dist/css/datepicker-bs4.css

@@ -0,0 +1,276 @@
+.datepicker {
+    display: none;
+}
+
+.datepicker.active {
+    display: block;
+}
+
+.datepicker-dropdown {
+    position: absolute;
+    top: 0;
+    left: 0;
+    z-index: 1000;
+    padding-top: 4px;
+}
+
+.datepicker-dropdown.datepicker-orient-top {
+    padding-top: 0;
+    padding-bottom: 4px;
+}
+
+.datepicker-picker {
+    display: inline-block;
+    border-radius: 0.25rem;
+    background-color: #fff;
+}
+
+.datepicker-dropdown .datepicker-picker {
+    box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.175);
+}
+
+.datepicker-picker span {
+    display: block;
+    flex: 1;
+    border: 0;
+    border-radius: 0.25rem;
+    cursor: default;
+    text-align: center;
+    -webkit-touch-callout: none;
+    -webkit-user-select: none;
+       -moz-user-select: none;
+        -ms-user-select: none;
+            user-select: none;
+}
+
+.datepicker-main {
+    padding: 2px;
+}
+
+.datepicker-footer {
+    box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
+    background-color: #f8f9fa;
+}
+
+.datepicker-controls, .datepicker-view, .datepicker-view .days-of-week, .datepicker-grid {
+    display: flex;
+}
+
+.datepicker-grid {
+    flex-wrap: wrap;
+}
+
+.datepicker-view .dow, .datepicker-view .days .datepicker-cell {
+    flex-basis: 14.28571%;
+}
+
+.datepicker-view.datepicker-grid .datepicker-cell {
+    flex-basis: 25%;
+}
+
+.datepicker-view .week, .datepicker-cell {
+    height: 2.25rem;
+    line-height: 2.25rem;
+}
+
+.datepicker-title {
+    box-shadow: inset 0 -1px 1px rgba(0, 0, 0, 0.1);
+    background-color: #f8f9fa;
+    padding: 0.375rem 0.75rem;
+    text-align: center;
+    font-weight: 700;
+}
+
+.datepicker-header .datepicker-controls {
+    padding: 2px 2px 0;
+}
+
+.datepicker-controls .btn {
+    border-color: #f8f9fa;
+    background-color: #fff;
+}
+
+.datepicker-controls .btn:hover {
+    border-color: #dae0e5;
+    background-color: #e2e6ea;
+    color: #212529;
+}
+
+.datepicker-controls .btn:focus {
+    border-color: #dae0e5;
+    box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5);
+    background-color: #e2e6ea;
+    color: #212529;
+}
+
+.datepicker-controls .btn:disabled {
+    border-color: #f8f9fa;
+    background-color: #f8f9fa;
+    color: #212529;
+}
+
+.datepicker-controls .btn:not(:disabled):active {
+    border-color: #d3d9df;
+    background-color: #dae0e5;
+    color: #212529;
+}
+
+.datepicker-controls .btn:not(:disabled):active:focus {
+    box-shadow: 0 0 0 0.2rem rgba(216, 217, 219, 0.5);
+}
+
+.datepicker-header .datepicker-controls .btn {
+    border-color: transparent;
+    font-weight: bold;
+}
+
+.datepicker-footer .datepicker-controls .btn {
+    margin: calc(0.375rem - 1px) 0.375rem;
+    border-radius: 0.2rem;
+    width: 100%;
+    font-size: 0.875rem;
+}
+
+.datepicker-controls .view-switch {
+    flex: auto;
+}
+
+.datepicker-controls .prev-btn,
+.datepicker-controls .next-btn {
+    padding-right: 0.375rem;
+    padding-left: 0.375rem;
+    width: 2.25rem;
+}
+
+.datepicker-controls .prev-btn.disabled,
+.datepicker-controls .next-btn.disabled {
+    visibility: hidden;
+}
+
+.datepicker-view .dow {
+    height: 1.5rem;
+    line-height: 1.5rem;
+    font-size: 0.9375rem;
+    font-weight: 700;
+}
+
+.datepicker-view .week {
+    width: 2.25rem;
+    color: #dee2e6;
+    font-size: 0.875rem;
+}
+
+@media (max-width: 22.5rem) {
+    .datepicker-view .week {
+        width: 1.96875rem;
+    }
+}
+
+.datepicker-grid {
+    width: 15.75rem;
+}
+
+@media (max-width: 22.5rem) {
+    .calendar-weeks + .days .datepicker-grid {
+        width: 13.78125rem;
+    }
+}
+
+.datepicker-cell:not(.disabled):hover {
+    background-color: #f9f9f9;
+    cursor: pointer;
+}
+
+.datepicker-cell.focused:not(.selected) {
+    background-color: #f1f3f5;
+}
+
+.datepicker-cell.selected, .datepicker-cell.selected:hover {
+    background-color: #007bff;
+    color: #fff;
+    font-weight: 600;
+}
+
+.datepicker-cell.disabled {
+    color: #6c757d;
+}
+
+.datepicker-cell.prev:not(.disabled), .datepicker-cell.next:not(.disabled) {
+    color: #6c757d;
+}
+
+.datepicker-cell.prev.selected, .datepicker-cell.next.selected {
+    color: #e6e6e6;
+}
+
+.datepicker-cell.highlighted:not(.selected):not(.range):not(.today) {
+    border-radius: 0;
+    background-color: #f8f9fa;
+}
+
+.datepicker-cell.highlighted:not(.selected):not(.range):not(.today):not(.disabled):hover {
+    background-color: #f1f3f5;
+}
+
+.datepicker-cell.highlighted:not(.selected):not(.range):not(.today).focused {
+    background-color: #f1f3f5;
+}
+
+.datepicker-cell.today:not(.selected) {
+    background-color: #20c997;
+}
+
+.datepicker-cell.today:not(.selected):not(.disabled) {
+    color: #fff;
+}
+
+.datepicker-cell.today.focused:not(.selected) {
+    background-color: #1ebe8f;
+}
+
+.datepicker-cell.range-start:not(.selected), .datepicker-cell.range-end:not(.selected) {
+    background-color: #6c757d;
+    color: #fff;
+}
+
+.datepicker-cell.range-start.focused:not(.selected), .datepicker-cell.range-end.focused:not(.selected) {
+    background-color: #666f76;
+}
+
+.datepicker-cell.range-start {
+    border-radius: 0.25rem 0 0 0.25rem;
+}
+
+.datepicker-cell.range-end {
+    border-radius: 0 0.25rem 0.25rem 0;
+}
+
+.datepicker-cell.range {
+    border-radius: 0;
+    background-color: #e9ecef;
+}
+
+.datepicker-cell.range:not(.disabled):not(.focused):not(.today):hover {
+    background-color: #e2e6ea;
+}
+
+.datepicker-cell.range.disabled {
+    color: #cbd3da;
+}
+
+.datepicker-cell.range.focused {
+    background-color: #dadfe4;
+}
+
+.datepicker-view.datepicker-grid .datepicker-cell {
+    height: 4.5rem;
+    line-height: 4.5rem;
+}
+
+.datepicker-input.in-edit {
+    border-color: #66b0ff;
+}
+
+.datepicker-input.in-edit:focus, .datepicker-input.in-edit:active {
+    box-shadow: 0 0 0.25em 0.25em rgba(102, 176, 255, 0.2);
+}

File diff suppressed because it is too large
+ 1 - 0
View/vanillajs-datepicker/dist/css/datepicker-bs4.min.css


+ 258 - 0
View/vanillajs-datepicker/dist/css/datepicker-bulma.css

@@ -0,0 +1,258 @@
+.datepicker {
+    display: none;
+}
+
+.datepicker.active {
+    display: block;
+}
+
+.datepicker-dropdown {
+    position: absolute;
+    top: 0;
+    left: 0;
+    z-index: 20;
+    padding-top: 4px;
+}
+
+.datepicker-dropdown.datepicker-orient-top {
+    padding-top: 0;
+    padding-bottom: 4px;
+}
+
+.datepicker-picker {
+    display: inline-block;
+    border-radius: 4px;
+    background-color: white;
+}
+
+.datepicker-dropdown .datepicker-picker {
+    box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);
+}
+
+.datepicker-picker span {
+    display: block;
+    flex: 1;
+    border: 0;
+    border-radius: 4px;
+    cursor: default;
+    text-align: center;
+    -webkit-touch-callout: none;
+    -webkit-user-select: none;
+       -moz-user-select: none;
+        -ms-user-select: none;
+            user-select: none;
+}
+
+.datepicker-main {
+    padding: 2px;
+}
+
+.datepicker-footer {
+    box-shadow: inset 0 1px 1px rgba(10, 10, 10, 0.1);
+    background-color: whitesmoke;
+}
+
+.datepicker-controls, .datepicker-view, .datepicker-view .days-of-week, .datepicker-grid {
+    display: flex;
+}
+
+.datepicker-grid {
+    flex-wrap: wrap;
+}
+
+.datepicker-view .dow, .datepicker-view .days .datepicker-cell {
+    flex-basis: 14.28571%;
+}
+
+.datepicker-view.datepicker-grid .datepicker-cell {
+    flex-basis: 25%;
+}
+
+.datepicker-view .week, .datepicker-cell {
+    height: 2.25rem;
+    line-height: 2.25rem;
+}
+
+.datepicker-title {
+    box-shadow: inset 0 -1px 1px rgba(10, 10, 10, 0.1);
+    background-color: whitesmoke;
+    padding: 0.375rem 0.75rem;
+    text-align: center;
+    font-weight: 700;
+}
+
+.datepicker-header .datepicker-controls {
+    padding: 2px 2px 0;
+}
+
+.datepicker-header .datepicker-controls .button {
+    border-color: transparent;
+    font-weight: bold;
+}
+
+.datepicker-header .datepicker-controls .button:hover {
+    background-color: #f9f9f9;
+}
+
+.datepicker-header .datepicker-controls .button:focus:not(:active) {
+    box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.25);
+}
+
+.datepicker-header .datepicker-controls .button:active {
+    background-color: #f2f2f2;
+}
+
+.datepicker-header .datepicker-controls .button[disabled] {
+    box-shadow: none;
+}
+
+.datepicker-footer .datepicker-controls .button {
+    margin: calc(0.375rem - 1px) 0.375rem;
+    border-radius: 2px;
+    width: 100%;
+    font-size: 0.75rem;
+}
+
+.datepicker-controls .view-switch {
+    flex: auto;
+}
+
+.datepicker-controls .prev-btn,
+.datepicker-controls .next-btn {
+    padding-right: 0.375rem;
+    padding-left: 0.375rem;
+    width: 2.25rem;
+}
+
+.datepicker-controls .prev-btn.disabled,
+.datepicker-controls .next-btn.disabled {
+    visibility: hidden;
+}
+
+.datepicker-view .dow {
+    height: 1.5rem;
+    line-height: 1.5rem;
+    font-size: 0.875rem;
+    font-weight: 700;
+}
+
+.datepicker-view .week {
+    width: 2.25rem;
+    color: #b5b5b5;
+    font-size: 0.75rem;
+}
+
+@media (max-width: 22.5rem) {
+    .datepicker-view .week {
+        width: 1.96875rem;
+    }
+}
+
+.datepicker-grid {
+    width: 15.75rem;
+}
+
+@media (max-width: 22.5rem) {
+    .calendar-weeks + .days .datepicker-grid {
+        width: 13.78125rem;
+    }
+}
+
+.datepicker-cell:not(.disabled):hover {
+    background-color: #f9f9f9;
+    cursor: pointer;
+}
+
+.datepicker-cell.focused:not(.selected) {
+    background-color: #e8e8e8;
+}
+
+.datepicker-cell.selected, .datepicker-cell.selected:hover {
+    background-color: #3273dc;
+    color: #fff;
+    font-weight: 600;
+}
+
+.datepicker-cell.disabled {
+    color: #dbdbdb;
+}
+
+.datepicker-cell.prev:not(.disabled), .datepicker-cell.next:not(.disabled) {
+    color: #7a7a7a;
+}
+
+.datepicker-cell.prev.selected, .datepicker-cell.next.selected {
+    color: #e6e6e6;
+}
+
+.datepicker-cell.highlighted:not(.selected):not(.range):not(.today) {
+    border-radius: 0;
+    background-color: whitesmoke;
+}
+
+.datepicker-cell.highlighted:not(.selected):not(.range):not(.today):not(.disabled):hover {
+    background-color: #eeeeee;
+}
+
+.datepicker-cell.highlighted:not(.selected):not(.range):not(.today).focused {
+    background-color: #e8e8e8;
+}
+
+.datepicker-cell.today:not(.selected) {
+    background-color: #00d1b2;
+}
+
+.datepicker-cell.today:not(.selected):not(.disabled) {
+    color: #fff;
+}
+
+.datepicker-cell.today.focused:not(.selected) {
+    background-color: #00c4a7;
+}
+
+.datepicker-cell.range-start:not(.selected), .datepicker-cell.range-end:not(.selected) {
+    background-color: #b5b5b5;
+    color: #fff;
+}
+
+.datepicker-cell.range-start.focused:not(.selected), .datepicker-cell.range-end.focused:not(.selected) {
+    background-color: #afafaf;
+}
+
+.datepicker-cell.range-start {
+    border-radius: 4px 0 0 4px;
+}
+
+.datepicker-cell.range-end {
+    border-radius: 0 4px 4px 0;
+}
+
+.datepicker-cell.range {
+    border-radius: 0;
+    background-color: #dbdbdb;
+}
+
+.datepicker-cell.range:not(.disabled):not(.focused):not(.today):hover {
+    background-color: #d5d5d5;
+}
+
+.datepicker-cell.range.disabled {
+    color: #c2c2c2;
+}
+
+.datepicker-cell.range.focused {
+    background-color: #cfcfcf;
+}
+
+.datepicker-view.datepicker-grid .datepicker-cell {
+    height: 4.5rem;
+    line-height: 4.5rem;
+}
+
+.datepicker-input.in-edit {
+    border-color: #2366d1;
+}
+
+.datepicker-input.in-edit:focus, .datepicker-input.in-edit:active {
+    box-shadow: 0 0 0.25em 0.25em rgba(35, 102, 209, 0.2);
+}

File diff suppressed because it is too large
+ 1 - 0
View/vanillajs-datepicker/dist/css/datepicker-bulma.min.css


+ 262 - 0
View/vanillajs-datepicker/dist/css/datepicker-foundation.css

@@ -0,0 +1,262 @@
+.datepicker {
+    display: none;
+}
+
+.datepicker.active {
+    display: block;
+}
+
+.datepicker-dropdown {
+    position: absolute;
+    top: 0;
+    left: 0;
+    z-index: 10;
+    padding-top: 4px;
+}
+
+.datepicker-dropdown.datepicker-orient-top {
+    padding-top: 0;
+    padding-bottom: 4px;
+}
+
+.datepicker-picker {
+    display: inline-block;
+    border-radius: 0;
+    background-color: #fefefe;
+}
+
+.datepicker-dropdown .datepicker-picker {
+    box-shadow: 0 0 0 1px #cacaca;
+}
+
+.datepicker-picker span {
+    display: block;
+    flex: 1;
+    border: 0;
+    border-radius: 0;
+    cursor: default;
+    text-align: center;
+    -webkit-touch-callout: none;
+    -webkit-user-select: none;
+       -moz-user-select: none;
+        -ms-user-select: none;
+            user-select: none;
+}
+
+.datepicker-main {
+    padding: 2px;
+}
+
+.datepicker-footer {
+    box-shadow: inset 0 1px 1px rgba(10, 10, 10, 0.1);
+    background-color: #e6e6e6;
+}
+
+.datepicker-controls, .datepicker-view, .datepicker-view .days-of-week, .datepicker-grid {
+    display: flex;
+}
+
+.datepicker-grid {
+    flex-wrap: wrap;
+}
+
+.datepicker-view .dow, .datepicker-view .days .datepicker-cell {
+    flex-basis: 14.28571%;
+}
+
+.datepicker-view.datepicker-grid .datepicker-cell {
+    flex-basis: 25%;
+}
+
+.datepicker-view .week, .datepicker-cell {
+    height: 2.25rem;
+    line-height: 2.25rem;
+}
+
+.datepicker-title {
+    box-shadow: inset 0 -1px 1px rgba(10, 10, 10, 0.1);
+    background-color: #e6e6e6;
+    padding: 0.375rem 0.75rem;
+    text-align: center;
+    font-weight: bold;
+}
+
+.datepicker-header .datepicker-controls {
+    padding: 2px 2px 0;
+}
+
+.datepicker-controls .button {
+    margin: 0;
+    background-color: #fefefe;
+    color: #0a0a0a;
+}
+
+.datepicker-controls .button:hover, .datepicker-controls .button:focus {
+    background-color: #d8d8d8;
+}
+
+.datepicker-controls .button:hover[disabled], .datepicker-controls .button:focus[disabled] {
+    opacity: 0.25;
+    background-color: #fefefe;
+    color: #0a0a0a;
+}
+
+.datepicker-header .datepicker-controls .button {
+    border-color: transparent;
+    font-weight: bold;
+}
+
+.datepicker-footer .datepicker-controls .button {
+    margin: calc(0.375rem - 1px) 0.375rem;
+    border-radius: 0;
+    width: 100%;
+    font-size: 0.75rem;
+}
+
+.datepicker-controls .view-switch {
+    flex: auto;
+}
+
+.datepicker-controls .prev-btn,
+.datepicker-controls .next-btn {
+    padding-right: 0.375rem;
+    padding-left: 0.375rem;
+    width: 2.25rem;
+}
+
+.datepicker-controls .prev-btn.disabled,
+.datepicker-controls .next-btn.disabled {
+    visibility: hidden;
+}
+
+.datepicker-view .dow {
+    height: 1.5rem;
+    line-height: 1.5rem;
+    font-size: 0.875rem;
+    font-weight: bold;
+}
+
+.datepicker-view .week {
+    width: 2.25rem;
+    color: #8a8a8a;
+    font-size: 0.75rem;
+}
+
+@media (max-width: 22.5rem) {
+    .datepicker-view .week {
+        width: 1.96875rem;
+    }
+}
+
+.datepicker-grid {
+    width: 15.75rem;
+}
+
+@media (max-width: 22.5rem) {
+    .calendar-weeks + .days .datepicker-grid {
+        width: 13.78125rem;
+    }
+}
+
+.datepicker-cell:not(.disabled):hover {
+    background-color: #f8f8f8;
+    cursor: pointer;
+}
+
+.datepicker-cell.focused:not(.selected) {
+    background-color: #f1f1f1;
+}
+
+.datepicker-cell.selected, .datepicker-cell.selected:hover {
+    background-color: #1779ba;
+    color: #fefefe;
+    font-weight: semibold;
+}
+
+.datepicker-cell.disabled {
+    color: #e6e6e6;
+}
+
+.datepicker-cell.prev:not(.disabled), .datepicker-cell.next:not(.disabled) {
+    color: #cacaca;
+}
+
+.datepicker-cell.prev.selected, .datepicker-cell.next.selected {
+    color: #e5e5e5;
+}
+
+.datepicker-cell.highlighted:not(.selected):not(.range):not(.today) {
+    border-radius: 0;
+    background-color: #f7f7f7;
+}
+
+.datepicker-cell.highlighted:not(.selected):not(.range):not(.today):not(.disabled):hover {
+    background-color: #f1f1f1;
+}
+
+.datepicker-cell.highlighted:not(.selected):not(.range):not(.today).focused {
+    background-color: #f1f1f1;
+}
+
+.datepicker-cell.today:not(.selected) {
+    background-color: #d7ecfa;
+}
+
+.datepicker-cell.today:not(.selected):not(.disabled) {
+    color: #8a8a8a;
+}
+
+.datepicker-cell.today.focused:not(.selected) {
+    background-color: #cbe7f9;
+}
+
+.datepicker-cell.range-start:not(.selected), .datepicker-cell.range-end:not(.selected) {
+    background-color: #767676;
+    color: #fefefe;
+}
+
+.datepicker-cell.range-start.focused:not(.selected), .datepicker-cell.range-end.focused:not(.selected) {
+    background-color: #707070;
+}
+
+.datepicker-cell.range-start {
+    border-radius: 0 0 0 0;
+}
+
+.datepicker-cell.range-end {
+    border-radius: 0 0 0 0;
+}
+
+.datepicker-cell.range {
+    border-radius: 0;
+    background-color: #e6e6e6;
+}
+
+.datepicker-cell.range:not(.disabled):not(.focused):not(.today):hover {
+    background-color: #e0e0e0;
+}
+
+.datepicker-cell.range.disabled {
+    color: #cdcdcd;
+}
+
+.datepicker-cell.range.focused {
+    background-color: #d9d9d9;
+}
+
+.datepicker-cell.range.today {
+    background-color: #b3dbf6;
+}
+
+.datepicker-view.datepicker-grid .datepicker-cell {
+    height: 4.5rem;
+    line-height: 4.5rem;
+}
+
+.datepicker-input.in-edit {
+    border-color: #a4a4a4;
+}
+
+.datepicker-input.in-edit:focus, .datepicker-input.in-edit:active {
+    box-shadow: 0 0 0.25em 0.25em rgba(164, 164, 164, 0.2);
+}

File diff suppressed because it is too large
+ 1 - 0
View/vanillajs-datepicker/dist/css/datepicker-foundation.min.css


+ 306 - 0
View/vanillajs-datepicker/dist/css/datepicker.css

@@ -0,0 +1,306 @@
+.datepicker {
+    display: none;
+}
+
+.datepicker.active {
+    display: block;
+}
+
+.datepicker-dropdown {
+    position: absolute;
+    top: 0;
+    left: 0;
+    z-index: 20;
+    padding-top: 4px;
+}
+
+.datepicker-dropdown.datepicker-orient-top {
+    padding-top: 0;
+    padding-bottom: 4px;
+}
+
+.datepicker-picker {
+    display: inline-block;
+    border-radius: 4px;
+    background-color: white;
+}
+
+.datepicker-dropdown .datepicker-picker {
+    box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1);
+}
+
+.datepicker-picker span {
+    display: block;
+    flex: 1;
+    border: 0;
+    border-radius: 4px;
+    cursor: default;
+    text-align: center;
+    -webkit-touch-callout: none;
+    -webkit-user-select: none;
+       -moz-user-select: none;
+        -ms-user-select: none;
+            user-select: none;
+}
+
+.datepicker-main {
+    padding: 2px;
+}
+
+.datepicker-footer {
+    box-shadow: inset 0 1px 1px rgba(10, 10, 10, 0.1);
+    background-color: whitesmoke;
+}
+
+.datepicker-controls, .datepicker-view, .datepicker-view .days-of-week, .datepicker-grid {
+    display: flex;
+}
+
+.datepicker-grid {
+    flex-wrap: wrap;
+}
+
+.datepicker-view .dow, .datepicker-view .days .datepicker-cell {
+    flex-basis: 14.28571%;
+}
+
+.datepicker-view.datepicker-grid .datepicker-cell {
+    flex-basis: 25%;
+}
+
+.datepicker-view .week, .datepicker-cell {
+    height: 2.25rem;
+    line-height: 2.25rem;
+}
+
+.datepicker-title {
+    box-shadow: inset 0 -1px 1px rgba(10, 10, 10, 0.1);
+    background-color: whitesmoke;
+    padding: 0.375rem 0.75rem;
+    text-align: center;
+    font-weight: 700;
+}
+
+.datepicker-header .datepicker-controls {
+    padding: 2px 2px 0;
+}
+
+.datepicker-controls .button {
+    display: inline-flex;
+    position: relative;
+    align-items: center;
+    justify-content: center;
+    margin: 0;
+    border: 1px solid #dbdbdb;
+    border-radius: 4px;
+    box-shadow: none;
+    background-color: white;
+    cursor: pointer;
+    padding: calc(0.375em - 1px) 0.75em;
+    height: 2.25em;
+    vertical-align: top;
+    text-align: center;
+    line-height: 1.5;
+    white-space: nowrap;
+    color: #363636;
+    font-size: 1rem;
+}
+
+.datepicker-controls .button:focus, .datepicker-controls .button:active {
+    outline: none;
+}
+
+.datepicker-controls .button:hover {
+    border-color: #b5b5b5;
+    color: #363636;
+}
+
+.datepicker-controls .button:focus {
+    border-color: #3273dc;
+    color: #363636;
+}
+
+.datepicker-controls .button:focus:not(:active) {
+    box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25);
+}
+
+.datepicker-controls .button:active {
+    border-color: #4a4a4a;
+    color: #363636;
+}
+
+.datepicker-controls .button[disabled] {
+    cursor: not-allowed;
+}
+
+.datepicker-header .datepicker-controls .button {
+    border-color: transparent;
+    font-weight: bold;
+}
+
+.datepicker-header .datepicker-controls .button:hover {
+    background-color: #f9f9f9;
+}
+
+.datepicker-header .datepicker-controls .button:focus:not(:active) {
+    box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.25);
+}
+
+.datepicker-header .datepicker-controls .button:active {
+    background-color: #f2f2f2;
+}
+
+.datepicker-header .datepicker-controls .button[disabled] {
+    box-shadow: none;
+}
+
+.datepicker-footer .datepicker-controls .button {
+    margin: calc(0.375rem - 1px) 0.375rem;
+    border-radius: 2px;
+    width: 100%;
+    font-size: 0.75rem;
+}
+
+.datepicker-controls .view-switch {
+    flex: auto;
+}
+
+.datepicker-controls .prev-btn,
+.datepicker-controls .next-btn {
+    padding-right: 0.375rem;
+    padding-left: 0.375rem;
+    width: 2.25rem;
+}
+
+.datepicker-controls .prev-btn.disabled,
+.datepicker-controls .next-btn.disabled {
+    visibility: hidden;
+}
+
+.datepicker-view .dow {
+    height: 1.5rem;
+    line-height: 1.5rem;
+    font-size: 0.875rem;
+    font-weight: 700;
+}
+
+.datepicker-view .week {
+    width: 2.25rem;
+    color: #b5b5b5;
+    font-size: 0.75rem;
+}
+
+@media (max-width: 22.5rem) {
+    .datepicker-view .week {
+        width: 1.96875rem;
+    }
+}
+
+.datepicker-grid {
+    width: 15.75rem;
+}
+
+@media (max-width: 22.5rem) {
+    .calendar-weeks + .days .datepicker-grid {
+        width: 13.78125rem;
+    }
+}
+
+.datepicker-cell:not(.disabled):hover {
+    background-color: #f9f9f9;
+    cursor: pointer;
+}
+
+.datepicker-cell.focused:not(.selected) {
+    background-color: #e8e8e8;
+}
+
+.datepicker-cell.selected, .datepicker-cell.selected:hover {
+    background-color: #3273dc;
+    color: #fff;
+    font-weight: 600;
+}
+
+.datepicker-cell.disabled {
+    color: #dbdbdb;
+}
+
+.datepicker-cell.prev:not(.disabled), .datepicker-cell.next:not(.disabled) {
+    color: #7a7a7a;
+}
+
+.datepicker-cell.prev.selected, .datepicker-cell.next.selected {
+    color: #e6e6e6;
+}
+
+.datepicker-cell.highlighted:not(.selected):not(.range):not(.today) {
+    border-radius: 0;
+    background-color: whitesmoke;
+}
+
+.datepicker-cell.highlighted:not(.selected):not(.range):not(.today):not(.disabled):hover {
+    background-color: #eeeeee;
+}
+
+.datepicker-cell.highlighted:not(.selected):not(.range):not(.today).focused {
+    background-color: #e8e8e8;
+}
+
+.datepicker-cell.today:not(.selected) {
+    background-color: #00d1b2;
+}
+
+.datepicker-cell.today:not(.selected):not(.disabled) {
+    color: #fff;
+}
+
+.datepicker-cell.today.focused:not(.selected) {
+    background-color: #00c4a7;
+}
+
+.datepicker-cell.range-start:not(.selected), .datepicker-cell.range-end:not(.selected) {
+    background-color: #b5b5b5;
+    color: #fff;
+}
+
+.datepicker-cell.range-start.focused:not(.selected), .datepicker-cell.range-end.focused:not(.selected) {
+    background-color: #afafaf;
+}
+
+.datepicker-cell.range-start {
+    border-radius: 4px 0 0 4px;
+}
+
+.datepicker-cell.range-end {
+    border-radius: 0 4px 4px 0;
+}
+
+.datepicker-cell.range {
+    border-radius: 0;
+    background-color: #dbdbdb;
+}
+
+.datepicker-cell.range:not(.disabled):not(.focused):not(.today):hover {
+    background-color: #d5d5d5;
+}
+
+.datepicker-cell.range.disabled {
+    color: #c2c2c2;
+}
+
+.datepicker-cell.range.focused {
+    background-color: #cfcfcf;
+}
+
+.datepicker-view.datepicker-grid .datepicker-cell {
+    height: 4.5rem;
+    line-height: 4.5rem;
+}
+
+.datepicker-input.in-edit {
+    border-color: #2366d1;
+}
+
+.datepicker-input.in-edit:focus, .datepicker-input.in-edit:active {
+    box-shadow: 0 0 0.25em 0.25em rgba(35, 102, 209, 0.2);
+}

File diff suppressed because it is too large
+ 1 - 0
View/vanillajs-datepicker/dist/css/datepicker.min.css


File diff suppressed because it is too large
+ 2757 - 0
View/vanillajs-datepicker/dist/js/datepicker-full.js


File diff suppressed because it is too large
+ 1 - 0
View/vanillajs-datepicker/dist/js/datepicker-full.min.js


File diff suppressed because it is too large
+ 2549 - 0
View/vanillajs-datepicker/dist/js/datepicker.js


File diff suppressed because it is too large
+ 1 - 0
View/vanillajs-datepicker/dist/js/datepicker.min.js


+ 15 - 0
View/vanillajs-datepicker/dist/js/locales/ar-tn.js

@@ -0,0 +1,15 @@
+/**
+ * Arabic-Tunisia translation for bootstrap-datepicker
+ * Souhaieb Besbes <besbes.souhaieb@gmail.com>
+ */
+(function () {
+  Datepicker.locales['ar-tn'] = {
+    days: ["الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت", "الأحد"],
+    daysShort: ["أحد", "اثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت", "أحد"],
+    daysMin: ["ح", "ن", "ث", "ع", "خ", "ج", "س", "ح"],
+    months: ["جانفي","فيفري","مارس","أفريل","ماي","جوان","جويليه","أوت","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],
+    monthsShort: ["جانفي","فيفري","مارس","أفريل","ماي","جوان","جويليه","أوت","سبتمبر","أكتوبر","نوفمبر","ديسمبر"],
+    today: "هذا اليوم",
+    rtl: true
+  };
+}());

+ 15 - 0
View/vanillajs-datepicker/dist/js/locales/ar.js

@@ -0,0 +1,15 @@
+/**
+ * Arabic translation for bootstrap-datepicker
+ * Mohammed Alshehri <alshehri866@gmail.com>
+ */
+(function () {
+  Datepicker.locales.ar = {
+    days: ["الأحد", "الاثنين", "الثلاثاء", "الأربعاء", "الخميس", "الجمعة", "السبت", "الأحد"],
+    daysShort: ["أحد", "اثنين", "ثلاثاء", "أربعاء", "خميس", "جمعة", "سبت", "أحد"],
+    daysMin: ["ح", "ن", "ث", "ع", "خ", "ج", "س", "ح"],
+    months: ["يناير", "فبراير", "مارس", "أبريل", "مايو", "يونيو", "يوليو", "أغسطس", "سبتمبر", "أكتوبر", "نوفمبر", "ديسمبر"],
+    monthsShort: ["يناير", "فبراير", "مارس", "أبريل", "مايو", "يونيو", "يوليو", "أغسطس", "سبتمبر", "أكتوبر", "نوفمبر", "ديسمبر"],
+    today: "هذا اليوم",
+    rtl: true
+  };
+}());

+ 14 - 0
View/vanillajs-datepicker/dist/js/locales/az.js

@@ -0,0 +1,14 @@
+// Azerbaijani
+(function () {
+  Datepicker.locales.az = {
+    days: ["Bazar", "Bazar ertəsi", "Çərşənbə axşamı", "Çərşənbə", "Cümə axşamı", "Cümə", "Şənbə"],
+    daysShort: ["B.", "B.e", "Ç.a", "Ç.", "C.a", "C.", "Ş."],
+    daysMin: ["B.", "B.e", "Ç.a", "Ç.", "C.a", "C.", "Ş."],
+    months: ["Yanvar", "Fevral", "Mart", "Aprel", "May", "İyun", "İyul", "Avqust", "Sentyabr", "Oktyabr", "Noyabr", "Dekabr"],
+    monthsShort: ["Yan", "Fev", "Mar", "Apr", "May", "İyun", "İyul", "Avq", "Sen", "Okt", "Noy", "Dek"],
+    today: "Bu gün",
+    weekStart: 1,
+    clear: "Təmizlə",
+    monthsTitle: 'Aylar'
+  };
+}());

+ 14 - 0
View/vanillajs-datepicker/dist/js/locales/bg.js

@@ -0,0 +1,14 @@
+/**
+ * Bulgarian translation for bootstrap-datepicker
+ * Apostol Apostolov <apostol.s.apostolov@gmail.com>
+ */
+(function () {
+  Datepicker.locales.bg = {
+    days: ["Неделя", "Понеделник", "Вторник", "Сряда", "Четвъртък", "Петък", "Събота"],
+    daysShort: ["Нед", "Пон", "Вто", "Сря", "Чет", "Пет", "Съб"],
+    daysMin: ["Н", "П", "В", "С", "Ч", "П", "С"],
+    months: ["Януари", "Февруари", "Март", "Април", "Май", "Юни", "Юли", "Август", "Септември", "Октомври", "Ноември", "Декември"],
+    monthsShort: ["Ян", "Фев", "Мар", "Апр", "Май", "Юни", "Юли", "Авг", "Сеп", "Окт", "Ное", "Дек"],
+    today: "днес"
+  };
+}());

+ 18 - 0
View/vanillajs-datepicker/dist/js/locales/bm.js

@@ -0,0 +1,18 @@
+/**
+ * Bamanankan (bm) translation for bootstrap-datepicker
+ * Fatou Fall <fatou@medicmobile.org>
+ */
+(function () {
+  Datepicker.locales.bm = {
+    days: ["Kari","Ntɛnɛn","Tarata","Araba","Alamisa","Juma","Sibiri"],
+    daysShort: ["Kar","Ntɛ","Tar","Ara","Ala","Jum","Sib"],
+    daysMin: ["Ka","Nt","Ta","Ar","Al","Ju","Si"],
+    months: ["Zanwuyekalo","Fewuruyekalo","Marisikalo","Awirilikalo","Mɛkalo","Zuwɛnkalo","Zuluyekalo","Utikalo","Sɛtanburukalo","ɔkutɔburukalo","Nowanburukalo","Desanburukalo"],
+    monthsShort: ["Zan","Few","Mar","Awi","Mɛ","Zuw","Zul","Uti","Sɛt","ɔku","Now","Des"],
+    today: "Bi",
+    monthsTitle: "Kalo",
+    clear: "Ka jɔsi",
+    weekStart: 1,
+    format: "dd/mm/yyyy"
+  };
+}());

+ 19 - 0
View/vanillajs-datepicker/dist/js/locales/bn.js

@@ -0,0 +1,19 @@
+/**
+ * Bengali (Bangla) translation for bootstrap-datepicker
+ * Karim Khan <kkhancse91@gmail.com>
+ * Orif N. Jr. <orif.zade@gmail.com>
+ */
+(function () {
+  Datepicker.locales.bn = {
+    days: ["রবিবার","সোমবার","মঙ্গলবার","বুধবার","বৃহস্পতিবার","শুক্রবার","শনিবার"],
+    daysShort: ["রবিবার","সোমবার","মঙ্গলবার","বুধবার","বৃহস্পতিবার","শুক্রবার","শনিবার"],
+    daysMin: ["রবি","সোম","মঙ্গল","বুধ","বৃহস্পতি","শুক্র","শনি"],
+    months: ["জানুয়ারী","ফেব্রুয়ারি","মার্চ","এপ্রিল","মে","জুন","জুলাই","অগাস্ট","সেপ্টেম্বর","অক্টোবর","নভেম্বর","ডিসেম্বর"],
+    monthsShort: ["জানুয়ারী","ফেব্রুয়ারি","মার্চ","এপ্রিল","মে","জুন","জুলাই","অগাস্ট","সেপ্টেম্বর","অক্টোবর","নভেম্বর","ডিসেম্বর"],
+    today: "আজ",
+    monthsTitle: "মাস",
+    clear: "পরিষ্কার",
+    weekStart: 0,
+    format: "mm/dd/yyyy"
+  };
+}());

+ 18 - 0
View/vanillajs-datepicker/dist/js/locales/br.js

@@ -0,0 +1,18 @@
+/**
+ * Breton translation for bootstrap-datepicker
+ * Gwenn Meynier <tornoz@laposte.net>
+ */
+(function () {
+  Datepicker.locales.br = {
+    days: ["Sul", "Lun", "Meurzh", "Merc'her", "Yaou", "Gwener", "Sadorn"],
+    daysShort: ["Sul", "Lun", "Meu.", "Mer.", "Yao.", "Gwe.", "Sad."],
+    daysMin: ["Su", "L", "Meu", "Mer", "Y", "G", "Sa"],
+    months: ["Genver", "C'hwevrer", "Meurzh", "Ebrel", "Mae", "Mezheven", "Gouere", "Eost", "Gwengolo", "Here", "Du", "Kerzu"],
+    monthsShort: ["Genv.", "C'hw.", "Meur.", "Ebre.", "Mae", "Mezh.", "Goue.", "Eost", "Gwen.", "Here", "Du", "Kerz."],
+    today: "Hiziv",
+    monthsTitle: "Miz",
+    clear: "Dilemel",
+    weekStart: 1,
+    format: "dd/mm/yyyy"
+  };
+}());

+ 0 - 0
View/vanillajs-datepicker/dist/js/locales/bs.js


Some files were not shown because too many files changed in this diff