diff --git a/composer.json b/composer.json index 81e7f38e3..4d87231a0 100644 --- a/composer.json +++ b/composer.json @@ -108,6 +108,7 @@ "Webkul\\Notification\\": "packages/Webkul/Notification/src", "Webkul\\Sitemap\\": "packages/Webkul/Sitemap/src", "Webkul\\Marketplace\\": "packages/Webkul/Marketplace/src", + "Webkul\\Bulkupload\\": "packages/Webkul/Bulkupload/src", "Sarga\\Shop\\": "packages/Sarga/Shop/src", "Sarga\\API\\": "packages/Sarga/API", "Sarga\\Admin\\": "packages/Sarga/Admin/src", diff --git a/config/concord.php b/config/concord.php index 5a76e239a..3f9012cd6 100755 --- a/config/concord.php +++ b/config/concord.php @@ -39,6 +39,6 @@ return [ \Webkul\User\Providers\ModuleServiceProvider::class, \Webkul\Velocity\Providers\ModuleServiceProvider::class, \Webkul\Sitemap\Providers\ModuleServiceProvider::class, - + \Webkul\Bulkupload\Providers\ModuleServiceProvider::class, ], ]; diff --git a/packages/Webkul/Bulkupload/.gitignore b/packages/Webkul/Bulkupload/.gitignore new file mode 100644 index 000000000..6903b57a0 --- /dev/null +++ b/packages/Webkul/Bulkupload/.gitignore @@ -0,0 +1 @@ +/package-lock.json \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/CHANGELOG for v1.3.x.md b/packages/Webkul/Bulkupload/CHANGELOG for v1.3.x.md new file mode 100644 index 000000000..caeaf3bb5 --- /dev/null +++ b/packages/Webkul/Bulkupload/CHANGELOG for v1.3.x.md @@ -0,0 +1,13 @@ +# CHANGELOG for v1.3.x + +#### This changelog consists the bug & security fixes and new features being included in the releases listed below. + +## **v1.3.3(24th of Oct, 2021)** - _Release_ + +- [compatible] Compatible with bagisto v1.3.3. + +## **v1.3.2(27th of Sept, 2021)** - _Release_ + +- [compatible] Compatible with bagisto v1.3.2. +- [fix] datagrid issue with bagisto v1.3.2. + diff --git a/packages/Webkul/Bulkupload/LICENSE b/packages/Webkul/Bulkupload/LICENSE new file mode 100644 index 000000000..3b04d4e20 --- /dev/null +++ b/packages/Webkul/Bulkupload/LICENSE @@ -0,0 +1,20 @@ +MIT License + +Copyright 2010-2020, Webkul Software + +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. \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/README.md b/packages/Webkul/Bulkupload/README.md new file mode 100644 index 000000000..7719ec0c2 --- /dev/null +++ b/packages/Webkul/Bulkupload/README.md @@ -0,0 +1,89 @@ +# Bagisto Bulk Upload + +By using this add-on, the admin can mass/bulk upload products of all types: simple, configurable, virtual, grouped, bundle, downloadable, booking. + +It packs in lots of demanding features that allows your business to scale in no time: + +- Product can be upload by CSV / XLS files. +- Data profile creation feature for admin. +- Each attribute has a different column. +- Image attachment with the product within CSV/XLS. +- If there is any error in the CSV / XLS file, then products will not be uploaded and hence customer/admin will come to know about the error. + +## Requirements: + +- **Bagisto**: v1.3.3 + +## Installation with composer: +- Run the following command +``` +composer require bagisto/bagisto-bulk-upload +``` + +- Goto vendor/bagisto/bagisto-bulkupload and copy the storage folder and merge it into the root of your project. + +- Goto config/concord.php file and add following line under 'modules' +```php +\Webkul\Bulkupload\Providers\ModuleServiceProvider::class +``` + +- Run these commands below to complete the setup +``` +composer dump-autoload +``` + +``` +php artisan migrate +php artisan storage:link +php artisan route:cache +php artisan config:cache +php artisan vendor:publish +``` +-> Press 0 and then press enter to publish all assets and configurations. + +## Installation without composer: + +- Unzip the respective extension zip and then merge "packages" and "storage" folders into project root directory. +- Goto config/app.php file and add following line under 'providers' + +``` +Webkul\Bulkupload\Providers\BulkUploadServiceProvider::class +``` + +- Goto composer.json file and add following line under 'psr-4' + +``` +"Webkul\\Bulkupload\\": "packages/Webkul/Bulkupload/src" +``` + +- Goto config/concord.php file and add following line under 'modules' + +```php +\Webkul\Bulkupload\Providers\ModuleServiceProvider::class +``` + +- Run these commands below to complete the setup + +``` +composer dump-autoload +``` + +``` +php artisan migrate +``` + +``` +php artisan storage:link +``` + +``` +php artisan route:cache +``` + +``` +php artisan vendor:publish + +-> Press 0 and then press enter to publish all assets and configurations. +``` + +> That's it, now just execute the project on your specified domain. diff --git a/packages/Webkul/Bulkupload/composer.json b/packages/Webkul/Bulkupload/composer.json new file mode 100644 index 000000000..e7ab19b4c --- /dev/null +++ b/packages/Webkul/Bulkupload/composer.json @@ -0,0 +1,36 @@ +{ + "name": "bagisto/bagisto-bulk-upload", + "description": "Add products using CSV or XLS in bagisto.", + "keywords": [ + "bagisto", + "product upload", + "bulk upload" + ], + "homepage": "https://github.com/bagisto/bagisto-bulk-upload", + "license": "MIT", + "authors": [ + { + "name": "Prateek Srivastava", + "email": "prateek.srivastava781@webkul.com", + "homepage": "https://bagisto.com", + "role": "Developer" + } + ], + "require": { + "konekt/concord": "^1.2" + }, + "autoload": { + "psr-4": { + "Webkul\\Bulkupload\\": "src" + } + }, + "extra": { + "laravel": { + "providers": [ + "Webkul\\Bulkupload\\Providers\\BulkUploadServiceProvider" + ], + "aliases": {} + } + }, + "minimum-stability": "dev" +} diff --git a/packages/Webkul/Bulkupload/package.json b/packages/Webkul/Bulkupload/package.json new file mode 100644 index 000000000..7b6644f4a --- /dev/null +++ b/packages/Webkul/Bulkupload/package.json @@ -0,0 +1,21 @@ +{ + "private": true, + "scripts": { + "dev": "npm run development", + "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", + "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", + "watch-poll": "cross-env npm run watch -- --watch-poll --progress", + "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", + "prod": "npm run production", + "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" + }, + "devDependencies": { + "axios": "^0.18", + "cross-env": "^5.1.4", + "laravel-mix": "^2.1", + "laravel-mix-merge-manifest": "^0.1.1", + "jquery": "^3.2", + "vue": "^2.1.10" + }, + "dependencies": {} +} diff --git a/packages/Webkul/Bulkupload/publishable/assets/css/bk_upload.css b/packages/Webkul/Bulkupload/publishable/assets/css/bk_upload.css new file mode 100644 index 000000000..f1e21d671 --- /dev/null +++ b/packages/Webkul/Bulkupload/publishable/assets/css/bk_upload.css @@ -0,0 +1 @@ +.bulk-upload-icon{background-image:url("../images/Icon-Bulk-Upload.svg")}.active .bulk-upload-icon,.bulk-upload-icon{width:48px;height:48px;display:inline-block;background-size:cover}.active .bulk-upload-icon{background-image:url("../images/Icon-Bulk-Upload-Active.svg")}progress{margin:15px 0 15px 34px;width:70%;height:15px}.icon{background-size:cover;display:inline-block}.icon.check-accent{width:22px;height:18px;background-image:url("../images/check-accent.svg")}.icon.icon-crossed{width:18px;height:15px;background-image:url("../images/Icon-Crossed.svg")}.icon.cross-accent{width:22px;height:18px;background-image:url("../images/cross-accent.svg")}.icon.finish-icon{width:22px;height:18px;background-image:url("../images/finish.svg")} \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/publishable/assets/css/bulk-admin.css b/packages/Webkul/Bulkupload/publishable/assets/css/bulk-admin.css new file mode 100644 index 000000000..1c7a1ddec --- /dev/null +++ b/packages/Webkul/Bulkupload/publishable/assets/css/bulk-admin.css @@ -0,0 +1,40 @@ +.bulk-upload-icon { + width: 48px; + height: 48px; + display: inline-block; + background-size: cover; + background-image: url("../images/Icon-Bulk-Upload.svg"); +} +.active.bulk-upload-icon, +.active .bulk-upload-icon { + background-image: url("../images/Icon-Bulk-Upload-Active.svg"); +} +progress { + margin: 15px 0 15px 34px; + width: 70%; + height: 15px; +} +.icon { + background-size: cover; + display: inline-block; +} +.icon.check-accent { + background-image: url("../images/check-accent.svg"); + width: 22px; + height: 18px; +} +.icon.icon-crossed { + width: 18px; + height: 15px; + background-image: url("../images/Icon-Crossed.svg"); +} +.icon.cross-accent { + width: 22px; + height: 18px; + background-image: url("../images/cross-accent.svg"); +} +.icon.finish-icon { + width: 22px; + height: 18px; + background-image: url("../images/finish.svg"); +} diff --git a/packages/Webkul/Bulkupload/publishable/assets/images/Icon-Bulk-Upload-Active.svg b/packages/Webkul/Bulkupload/publishable/assets/images/Icon-Bulk-Upload-Active.svg new file mode 100644 index 000000000..db0b09c72 --- /dev/null +++ b/packages/Webkul/Bulkupload/publishable/assets/images/Icon-Bulk-Upload-Active.svg @@ -0,0 +1,16 @@ + + + + Icon-Bulk-Upload-Active + Created with Sketch. + + + + + + + + + + + \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/publishable/assets/images/Icon-Bulk-Upload.svg b/packages/Webkul/Bulkupload/publishable/assets/images/Icon-Bulk-Upload.svg new file mode 100644 index 000000000..e987ea4e0 --- /dev/null +++ b/packages/Webkul/Bulkupload/publishable/assets/images/Icon-Bulk-Upload.svg @@ -0,0 +1,16 @@ + + + + Icon-Bulk-Upload + Created with Sketch. + + + + + + + + + + + \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/publishable/assets/images/Icon-Crossed.svg b/packages/Webkul/Bulkupload/publishable/assets/images/Icon-Crossed.svg new file mode 100644 index 000000000..038759b1b --- /dev/null +++ b/packages/Webkul/Bulkupload/publishable/assets/images/Icon-Crossed.svg @@ -0,0 +1,10 @@ + + + + Icon-Crossed + Created with Sketch. + + + + + \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/publishable/assets/images/check-accent.svg b/packages/Webkul/Bulkupload/publishable/assets/images/check-accent.svg new file mode 100644 index 000000000..8334c776f --- /dev/null +++ b/packages/Webkul/Bulkupload/publishable/assets/images/check-accent.svg @@ -0,0 +1,10 @@ + + + + Check-Accent + Created with Sketch. + + + + + \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/publishable/assets/images/cross-accent.svg b/packages/Webkul/Bulkupload/publishable/assets/images/cross-accent.svg new file mode 100644 index 000000000..be351129d --- /dev/null +++ b/packages/Webkul/Bulkupload/publishable/assets/images/cross-accent.svg @@ -0,0 +1,11 @@ + + + + Cross-Accent + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/publishable/assets/images/finish.svg b/packages/Webkul/Bulkupload/publishable/assets/images/finish.svg new file mode 100644 index 000000000..2401834e4 --- /dev/null +++ b/packages/Webkul/Bulkupload/publishable/assets/images/finish.svg @@ -0,0 +1,20 @@ + + + + tab/heading/icon/finish + Created with Sketch. + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/publishable/assets/mix-manifest.json b/packages/Webkul/Bulkupload/publishable/assets/mix-manifest.json new file mode 100644 index 000000000..c4d4c6263 --- /dev/null +++ b/packages/Webkul/Bulkupload/publishable/assets/mix-manifest.json @@ -0,0 +1,4 @@ +{ + "/css/bk_upload.css": "/css/bk_upload.css?id=b316e32901a4fff5f3a0", + "/css/bulk-admin.css": "/css/bulk-admin.css?id=d3639ab33677e9640729" +} diff --git a/packages/Webkul/Bulkupload/src/Config/admin-menu.php b/packages/Webkul/Bulkupload/src/Config/admin-menu.php new file mode 100644 index 000000000..6652a2bf3 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Config/admin-menu.php @@ -0,0 +1,29 @@ + 'catalog.bulkupload', + 'name' => 'bulkupload::app.admin.bulk-upload.manage-bulk-upload', + 'route' => 'admin.dataflow-profile.index', + 'sort' => 5, + 'icon-class' => '', + ], [ + 'key' => 'catalog.bulkupload.data-flow-profile', + 'name' => 'bulkupload::app.admin.bulk-upload.data-flow-profile.index', + 'route' => 'admin.dataflow-profile.index', + 'sort' => 1, + 'icon-class' => '', + ], [ + 'key' => 'catalog.bulkupload.upload-files', + 'name' => 'bulkupload::app.admin.bulk-upload.upload-files.index', + 'route' => 'admin.bulk-upload.index', + 'sort' => 2, + 'icon-class' => '', + ], [ + 'key' => 'catalog.bulkupload.run-profile', + 'name' => 'bulkupload::app.admin.bulk-upload.run-profile.index', + 'route' => 'admin.run-profile.index', + 'sort' => 3, + 'icon-class' => '', + ] +]; diff --git a/packages/Webkul/Bulkupload/src/Config/system.php b/packages/Webkul/Bulkupload/src/Config/system.php new file mode 100644 index 000000000..7d7d288e4 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Config/system.php @@ -0,0 +1,26 @@ + 'bulkupload', + 'name' => 'bulkupload::app.admin.system.bulkupload', + 'sort' => 5 + ], [ + 'key' => 'bulkupload.settings', + 'name' => 'bulkupload::app.admin.system.settings', + 'sort' => 1, + ], [ + 'key' => 'bulkupload.settings.general', + 'name' => 'bulkupload::app.admin.system.general', + 'sort' => 1, + 'fields' => [ + [ + 'name' => 'status', + 'title' => 'bulkupload::app.admin.system.status', + 'type' => 'boolean', + 'channel_based' => true, + 'locale_based' => false + ] + ] + ] +]; \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Contracts/DataFlowProfile.php b/packages/Webkul/Bulkupload/src/Contracts/DataFlowProfile.php new file mode 100644 index 000000000..0f1f4ff1b --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Contracts/DataFlowProfile.php @@ -0,0 +1,7 @@ +leftJoin('attribute_families', 'bulkupload_data_flow_profiles.attribute_family_id', '=', 'attribute_families.id') + ->select('bulkupload_data_flow_profiles.id', + 'bulkupload_data_flow_profiles.name as profile_name', 'attribute_families.name', 'bulkupload_data_flow_profiles.locale_code', 'bulkupload_data_flow_profiles.created_at'); + + $this->addFilter('created_at', 'bulkupload_data_flow_profiles.created_at'); + $this->addFilter('profile_name', 'bulkupload_data_flow_profiles.name'); + $this->addFilter('name', 'attribute_families.name'); + + $this->setQueryBuilder($queryBuilder); + } + + public function addColumns() + { + $this->addColumn([ + 'index' => 'profile_name', + 'label' => trans('bulkupload::app.admin.bulk-upload.data-flow-profile.name'), + 'type' => 'string', + 'searchable' => true, + 'sortable' => true, + 'filterable' => true + ]); + + $this->addColumn([ + 'index' => 'name', + 'label' => trans('admin::app.catalog.products.familiy'), + 'type' => 'string', + 'searchable' => true, + 'sortable' => true, + 'filterable' => true + ]); + + $this->addColumn([ + 'index' => 'locale_code', + 'label' => trans('bulkupload::app.admin.bulk-upload.data-flow-profile.data-grid.locale_code'), + 'type' => 'string', + 'searchable' => true, + 'sortable' => true, + 'filterable' => true + ]); + + $this->addColumn([ + 'index' => 'created_at', + 'label' => trans('bulkupload::app.admin.bulk-upload.data-flow-profile.data-grid.created-at'), + 'type' => 'datetime', + 'sortable' => true, + 'searchable' => false, + 'filterable' => true + ]); + } + + public function prepareActions() + { + $this->addAction([ + 'type' => 'Edit', + 'method' => 'GET', + 'route' => 'bulkupload.admin.profile.edit', + 'icon' => 'icon pencil-lg-icon', + 'title' => '' + + ]); + + $this->addAction([ + 'type' => trans('admin::app.datagrid.delete'), + 'method' => 'POST', + 'route' => 'bulkupload.admin.profile.delete', + 'confirm_text' => trans('ui::app.datagrid.massaction.delete'), + 'icon' => 'icon trash-icon', + 'title' => '' + ]); + + $this->enableAction = true; + } + + public function prepareMassActions() + { + $this->addMassAction([ + 'type' => 'delete', + 'label' => 'Delete', + 'action' => route('bulkupload.admin.profile.massDelete'), + 'method' => 'POST', + 'title' => '' + ]); + } +} diff --git a/packages/Webkul/Bulkupload/src/Database/Migrations/2019_04_23_115227_create_bulkupload_data_flow_profiles_table.php b/packages/Webkul/Bulkupload/src/Database/Migrations/2019_04_23_115227_create_bulkupload_data_flow_profiles_table.php new file mode 100644 index 000000000..bfb110a4c --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Database/Migrations/2019_04_23_115227_create_bulkupload_data_flow_profiles_table.php @@ -0,0 +1,35 @@ +increments('id'); + $table->string('name'); + $table->integer('attribute_family_id')->unsigned(); + $table->foreign('attribute_family_id', 'bulkupload_foreign_attribute_family_id')->references('id')->on('attribute_families')->onDelete('cascade'); + $table->boolean('run_status')->default(0); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('bulkupload_data_flow_profiles'); + } +} diff --git a/packages/Webkul/Bulkupload/src/Database/Migrations/2020_05_13_223920_create_import_products_table.php b/packages/Webkul/Bulkupload/src/Database/Migrations/2020_05_13_223920_create_import_products_table.php new file mode 100644 index 000000000..4dd9b5123 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Database/Migrations/2020_05_13_223920_create_import_products_table.php @@ -0,0 +1,50 @@ +increments('id'); + + $table->integer('attribute_family_id')->unsigned(); + $table->foreign('attribute_family_id', 'import_admin_foreign_attribute_family_id')->references('id')->on('attribute_families')->onDelete('cascade'); + + $table->integer('data_flow_profile_id')->unsigned(); + $table->foreign('data_flow_profile_id', 'import_admin_foreign_data_flow_profile_id')->references('id')->on('bulkupload_data_flow_profiles')->onDelete('cascade'); + + $table->boolean('is_downloadable')->default(0); + $table->string('upload_link_files'); + + $table->boolean('is_links_have_samples')->default(0); + $table->string('upload_link_sample_files'); + + $table->boolean('is_samples_available')->default(0); + $table->string('upload_sample_files'); + + $table->string('file_path'); + $table->string('image_path'); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('import_products'); + } +} diff --git a/packages/Webkul/Bulkupload/src/Database/Migrations/2021_12_28_150311_alter_bulkupload_data_flow_profiles_table.php b/packages/Webkul/Bulkupload/src/Database/Migrations/2021_12_28_150311_alter_bulkupload_data_flow_profiles_table.php new file mode 100644 index 000000000..7d0e24ef7 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Database/Migrations/2021_12_28_150311_alter_bulkupload_data_flow_profiles_table.php @@ -0,0 +1,30 @@ +string('locale_code'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + // + } +} diff --git a/packages/Webkul/Bulkupload/src/Helpers/ImportProduct.php b/packages/Webkul/Bulkupload/src/Helpers/ImportProduct.php new file mode 100644 index 000000000..31b82f20a --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Helpers/ImportProduct.php @@ -0,0 +1,163 @@ +importProductRepository = $importProductRepository; + + $this->dataFlowProfileRepository = $dataFlowProfileRepository; + } + + /** + * store import products for profile execution + * + * @return \Illuminate\Http\Response + */ + public function store() + { + request()->validate ([ + 'attribute_family' => 'required', + 'file_path' => 'required', + 'image_path' => 'mimetypes:application/zip|max:10000', + 'data_flow_profile' => 'required', + ]); + + $valid_extension = ['csv', 'xls', 'xlsx']; + $valid_image_extension = ['zip', 'rar']; + + $imageDir = 'imported-products/admin/images'; + $fileDir = 'imported-products/admin/files'; + $linkFilesDir = 'imported-products/admin/link-files'; + $linkSampleFilesDir = 'imported-products/admin/link-sample-files'; + $sampleFileDir = 'imported-products/admin/sample-file'; + + $attribute_family_id = request()->attribute_family; + $data_flow_profile_id = request()->data_flow_profile; + + $image = request()->file('image_path'); + $file = request()->file('file_path'); + $linkFiles = request()->file('link_files'); + $linkSampleFiles = request()->file('link_sample_files'); + $sampleFile = request()->file('sample_file'); + + if (! isset($image)) { + $image = ''; + } + + if (request()->is_downloadable) { + $product['is_downloadable'] = 1; + + if (! empty($linkFiles) && in_array($linkFiles->getClientOriginalExtension(), $valid_image_extension)) { + $uploadedLinkFiles = $linkFiles->storeAs($linkFilesDir, uniqid().'.'.$linkFiles->getClientOriginalExtension()); + + $product['upload_link_files'] = $uploadedLinkFiles; + } else { + session()->flash('error', trans('bulkupload::app.admin.bulk-upload.messages.file-format-error')); + + return redirect()->route('admin.bulk-upload.index'); + } + + if (request()->is_link_have_sample) { + $product['is_links_have_samples'] = 1; + + if (in_array($linkSampleFiles->getClientOriginalExtension(), $valid_image_extension)) { + $uploadedLinkSampleFiles = $linkSampleFiles->storeAs($linkSampleFilesDir, uniqid().'.'.$linkSampleFiles->getClientOriginalExtension()); + + $product['upload_link_sample_files'] = $uploadedLinkSampleFiles; + } else { + session()->flash('error', trans('bulkupload::app.admin.bulk-upload.messages.file-format-error')); + + return redirect()->route('admin.bulk-upload.index'); + } + } + + if (request()->is_sample) { + $product['is_samples_available'] = 1; + + if (in_array($sampleFile->getClientOriginalExtension(), $valid_image_extension)) { + $uploadedSampleFiles = $sampleFile->storeAs($sampleFileDir, uniqid().'.'.$sampleFile->getClientOriginalExtension()); + + $product['upload_sample_files'] = $uploadedSampleFiles; + } else { + session()->flash('error', trans('bulkupload::app.admin.bulk-upload.messages.file-format-error')); + + return redirect()->route('admin.bulk-upload.index'); + } + } + } + + $product['data_flow_profile_id'] = $data_flow_profile_id; + $product['attribute_family_id'] = $attribute_family_id; + + if ((! empty($image) && in_array($image->getClientOriginalExtension(), $valid_image_extension)) && (in_array($file->getClientOriginalExtension(), $valid_extension))) { + $uploadedImage = $image->storeAs($imageDir, uniqid().'.'.$image->getClientOriginalExtension()); + + $product['image_path'] = $uploadedImage; + + $uploadedFile = $file->storeAs($fileDir, uniqid().'.'.$file->getClientOriginalExtension()); + + $product['file_path'] = $uploadedFile; + } else if ( empty($image) && (in_array($file->getClientOriginalExtension(), $valid_extension))) { + $product['image_path'] = ''; + + $uploadedFile = $file->storeAs($fileDir, uniqid().'.'.$file->getClientOriginalExtension()); + + $product['file_path'] = $uploadedFile; + } else { + session()->flash('error', trans('bulkupload::app.admin.bulk-upload.messages.file-format-error')); + + return redirect()->route('admin.bulk-upload.index'); + } + + $data = $this->importProductRepository->findOneByField('data_flow_profile_id', $data_flow_profile_id); + + if ($data) { + $this->dataFlowProfileRepository->update(['run_status' => '0'], $data_flow_profile_id); + + $this->importProductRepository->Update($product, $data->id); + + session()->flash('success',trans('bulkupload::app.admin.bulk-upload.messages.update-profile')); + + return redirect()->route('admin.bulk-upload.index'); + } else { + $importNewProductsStore = $this->importProductRepository->create($product); + + session()->flash('success',trans('bulkupload::app.admin.bulk-upload.messages.profile-saved')); + + return redirect()->route('admin.bulk-upload.index'); + } + } +} \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Http/Controllers/Admin/BulkUploadController.php b/packages/Webkul/Bulkupload/src/Http/Controllers/Admin/BulkUploadController.php new file mode 100644 index 000000000..62966a15d --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Http/Controllers/Admin/BulkUploadController.php @@ -0,0 +1,188 @@ +_config = request('_config'); + + $this->attributeFamilyRepository = $attributeFamilyRepository; + + $this->importProductRepository = $importProductRepository; + + $this->dataFlowProfileRepository = $dataFlowProfileRepository; + } + + /** + * Display a listing of the resource. + * + * @return \Illuminate\View\View + */ + public function index() + { + $profiles = null; + $families = $this->attributeFamilyRepository->all(); + $allProfiles = $this->importProductRepository->get()->toArray(); + + if (! empty($allProfiles)) { + foreach ($allProfiles as $allProfile) { + $profilers[] = $allProfile['data_flow_profile_id']; + } + + foreach ($profilers as $key => $profiler) { + $profiles[] = $this->dataFlowProfileRepository->findByfield(['id' => $profilers[$key], 'run_status' => '0']); + } + } + + return view($this->_config['view'], compact('families', 'profiles')); + } + + /** + * Store a newly created resource in storage. + * + * @return \Illuminate\Http\Response + */ + public function store() + { + request()->validate([ + 'name' => 'required|unique:bulkupload_data_flow_profiles', + 'attribute_family' => 'required', + 'locale_code' => 'required' + ]); + + $dataFlowProfileAdmin['name'] = request()->name; + $dataFlowProfileAdmin['attribute_family_id'] = request()->attribute_family; + $dataFlowProfileAdmin['locale_code'] = request()->locale_code; + + + $this->dataFlowProfileRepository->create($dataFlowProfileAdmin); + + session()->flash('success',trans('bulkupload::app.admin.bulk-upload.messages.profile-saved')); + + return redirect()->route('admin.dataflow-profile.index'); + } + + /** + * Show the form for editing the specified resource. + * + * @param int $id + * @return \Illuminate\View\View + */ + public function edit($id) + { + $families = $this->attributeFamilyRepository->all(); + $profiles = $this->dataFlowProfileRepository->findOrFail($id); + + return view($this->_config['view'], compact('families', 'profiles')); + } + + /** + * Update the specified resource in storage. + * + * @param int $id + * @return \Illuminate\Http\Response + */ + public function update($id) + { + $product = $this->dataFlowProfileRepository->update(request()->except('_token'), $id); + $families = $this->attributeFamilyRepository->all(); + $profiles = $this->dataFlowProfileRepository->findOrFail($id); + + session()->flash('success', trans('admin::app.response.update-success', ['name' => 'Product'])); + + return redirect()->route('admin.dataflow-profile.index'); + } + + /** + * Remove the specified resource from storage. + * + * @param int $id + * @return \Illuminate\Http\Response + */ + public function destroy($id) + { + $product = $this->dataFlowProfileRepository->findOrFail($id)->delete(); + + session()->flash('success',trans('bulkupload::app.admin.bulk-upload.messages.profile-deleted')); + + return response()->json(['message' => true], 200); + } + + /** + * Mass Delete the dataflowprofiles + * + * @return \Illuminate\Http\Response + */ + public function massDestroy() + { + $profileIds = explode(',', request()->input('indexes')); + + foreach ($profileIds as $profileId) { + $profile = $this->dataFlowProfileRepository->find($profileId); + + if (isset($profile)) { + $this->dataFlowProfileRepository->delete($profileId); + } + } + + session()->flash('success', trans('admin::app.catalog.products.mass-delete-success')); + + return redirect()->route($this->_config['redirect']); + } +} \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Http/Controllers/Admin/Controller.php b/packages/Webkul/Bulkupload/src/Http/Controllers/Admin/Controller.php new file mode 100644 index 000000000..59702431b --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Http/Controllers/Admin/Controller.php @@ -0,0 +1,13 @@ +importProductRepository = $importProductRepository; + + $this->dataFlowProfileRepository = $dataFlowProfileRepository; + + $this->simpleProductRepository = $simpleProductRepository; + + $this->configurableProductRepository = $configurableProductRepository; + + $this->virtualProductRepository = $virtualProductRepository; + + $this->downloadableProductRepository = $downloadableProductRepository; + + $this->bundledProductRepository = $bundledProductRepository; + + $this->bookingProductRepository = $bookingProductRepository; + + $this->groupedProductRepository = $groupedProductRepository; + + $this->importProduct = $importProduct; + } + + /** + * Download sample files. + * + * @return \Illuminate\Http\Response + */ + public function downloadFile() + { + $items = []; + + foreach (config('product_types') as $item) { + $item['children'] = []; + + array_push($items, $item); + } + + $types = core()->sortItems($items); + + foreach ($types as $key => $productType) { + if (request()->download_sample == $key.'-csv') { + return response()->download(public_path('storage/downloads/sample-files/bulk'.$key.'productupload.csv')); + } else if (request()->download_sample == $key.'-xls') { + return response()->download(public_path('storage/downloads/sample-files/bulk'.$key.'productupload.xlsx')); + } else if (empty(request()->download_sample)) { + return redirect()->back(); + } + } + } + + /** + * Get profiles on basis of attribute family + * + * @return array + */ + public function getAllDataFlowProfiles() + { + $attribute_family_id = request()->attribute_family_id; + + $dataFlowProfiles = $this->dataFlowProfileRepository->findByField('attribute_family_id', request()->attribute_family_id); + + return ['dataFlowProfiles' => $dataFlowProfiles]; + } + + /** + * Read count of records in CSV/XLSX + * + * @return \Illuminate\Http\Response + */ + public function readCSVData() + { + $countCSV = 0; + + $dataFlowProfileRecord = $this->importProductRepository->findOneByField('data_flow_profile_id', request()->data_flow_profile_id); + + $this->dataFlowProfileRepository->update(['run_status' => '1'], request()->data_flow_profile_id); + + if ($dataFlowProfileRecord) { + $csvData = (new DataGridImport)->toArray($dataFlowProfileRecord->file_path)[0]; + + for ($i = 0; $i < count($csvData); $i++) { + if ($csvData[$i]['type'] == 'configurable') { + $countCSV += 1; + } else if ($csvData[0]['type'] != 'configurable') { + $countCSV = count($csvData); + } + } + + return $countCSV; + } + } + + /** + * store import products for profile execution + * + * @return \Illuminate\Http\Response + */ + public function importNewProductsStore() + { + $dataFlowProfileId = request()->data_flow_profile; + + if ($dataFlowProfileId) { + $importedProducts = $this->importProduct->store(); + + return $importedProducts; + } else { + session()->flash('error', trans('bulkupload::app.admin.bulk-upload.messages.data-profile-not-selected')); + + return back(); + } + } + + /** + * profile execution to upload products + * + * @return \Illuminate\Http\Response + */ + public function runProfile() + { + $data_flow_profile_id = request()->data_flow_profile_id; + $numberOfCSVRecord = request()->numberOfCSVRecord; + $countOfStartedProfiles = request()->countOfStartedProfiles; + $product = []; + $imageZipName = null; + + $dataFlowProfileRecord = $this->importProductRepository->findOneByField + ('data_flow_profile_id', $data_flow_profile_id); + + if ($dataFlowProfileRecord) { + $csvData = (new DataGridImport)->toArray($dataFlowProfileRecord->file_path)[0]; + + if (isset($dataFlowProfileRecord->image_path) && ($dataFlowProfileRecord->image_path != "") ) { + $imageZipName = $this->storeImageZip($dataFlowProfileRecord); + } + + if ($numberOfCSVRecord >= 0) { + for ($i = $countOfStartedProfiles; $i < count($csvData); $i++) { + $product['loopCount'] = $i; + + switch($csvData[$i]['type']) { + case "simple": + $simpleProduct = $this->simpleProductRepository->createProduct(request()->all(), $imageZipName, $product); + + return response()->json($simpleProduct); + + case "virtual": + $virtualProduct = $this->virtualProductRepository->createProduct(request()->all(), $imageZipName); + + return response()->json($virtualProduct); + case "downloadable": + $downloadableProduct = $this->downloadableProductRepository->createProduct(request()->all(), $imageZipName); + + return response()->json($downloadableProduct); + case "grouped": + $groupedProduct = $this->groupedProductRepository->createProduct(request()->all(), $imageZipName); + + return response()->json($groupedProduct); + case "booking": + $bookingProduct = $this->bookingProductRepository->createProduct(request()->all(), $imageZipName); + + return response()->json($bookingProduct); + case "bundle": + $bundledProduct = $this->bundledProductRepository->createProduct(request()->all(), $imageZipName); + + return response()->json($bundledProduct); + case "configurable" OR "variant": + $configurableProduct = $this->configurableProductRepository->createProduct(request()->all(), $imageZipName, $product); + + return response()->json($configurableProduct); + } + } + } else { + return response()->json([ + "success" => true, + "message" => "CSV Product Successfully Imported" + ]); + } + } + } + + public function storeImageZip($dataFlowProfileRecord) + { + $imageZip = new \ZipArchive(); + + $extractedPath = storage_path('app/public/imported-products/extracted-images/admin/'.$dataFlowProfileRecord->id.'/'); + + if ($imageZip->open(storage_path('app/public/'.$dataFlowProfileRecord->image_path))) { + for ($i = 0; $i < $imageZip->numFiles; $i++) { + $filename = $imageZip->getNameIndex($i); + $imageZipName = pathinfo($filename); + } + + $imageZip->extractTo($extractedPath); + $imageZip->close(); + } + + $listOfImages = scandir($extractedPath.$imageZipName['dirname'].'/'); + + foreach ($listOfImages as $key => $imageName) { + if (preg_match_all('/[\'"]/', $imageName)) { + $fileName = preg_replace('/[\'"]/', '',$imageName); + + rename($extractedPath.$imageZipName['dirname'].'/'.$imageName, $extractedPath.$imageZipName['dirname'].'/'.$fileName); + } + } + + return $imageZipName; + } +} diff --git a/packages/Webkul/Bulkupload/src/Http/admin-routes.php b/packages/Webkul/Bulkupload/src/Http/admin-routes.php new file mode 100644 index 000000000..7f97b0a73 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Http/admin-routes.php @@ -0,0 +1,65 @@ + ['web']], function () { + + Route::prefix(config('app.admin_url'))->group(function () { + + Route::group(['middleware' => ['admin']], function () { + Route::prefix('bulkupload')->group(function () { + + // Bulk Upload Products + Route::get('/upload-files', 'Webkul\Bulkupload\Http\Controllers\Admin\BulkUploadController@index')->defaults('_config', [ + 'view' => 'bulkupload::admin.bulk-upload.upload-files.index' + ])->name('admin.bulk-upload.index'); + + Route::get('/run-profile', 'Webkul\Bulkupload\Http\Controllers\Admin\BulkUploadController@index')->defaults('_config', [ + 'view' => 'bulkupload::admin.bulk-upload.run-profile.index' + ])->name('admin.run-profile.index'); + + Route::post('/read-csv', 'Webkul\Bulkupload\Http\Controllers\Admin\HelperController@readCSVData') + ->name('bulk-upload-admin.read-csv'); + + Route::post('/getprofiles', 'Webkul\Bulkupload\Http\Controllers\Admin\HelperController@getAllDataFlowProfiles') + ->name('bulk-upload-admin.get-all-profile'); + + // Download Sample Files + Route::post('/download','Webkul\Bulkupload\Http\Controllers\Admin\HelperController@downloadFile')->defaults('_config',[ + 'view' => 'bulkupload::admin.bulk-upload.upload-files.index' + ])->name('download-sample-files'); + + // import new products + Route::post('/importnew', 'Webkul\Bulkupload\Http\Controllers\Admin\HelperController@importNewProductsStore')->defaults('_config',['view' => 'bulkupload::admin.bulk-upload.upload-files.index' ])->name('import-new-products-form-submit'); + + Route::prefix('dataflowprofile')->group(function () { + Route::get('/', 'Webkul\Bulkupload\Http\Controllers\Admin\BulkUploadController@index')->defaults('_config', [ + 'view' => 'bulkupload::admin.bulk-upload.data-flow-profile.index' + ])->name('admin.dataflow-profile.index'); + + Route::post('/addprofile', 'Webkul\Bulkupload\Http\Controllers\Admin\BulkUploadController@store')->defaults('_config', [ + 'view' => 'bulkupload::admin.bulk-upload.data-flow-profile.index' + ])->name('bulkupload.bulk-upload.dataflow.add-profile'); + + Route::post('/runprofile', 'Webkul\Bulkupload\Http\Controllers\Admin\HelperController@runProfile')->defaults('_config', [ + 'view' => 'bulkupload::admin.bulk-upload.run-profile.progressbar' + ])->name('bulk-upload-admin.run-profile'); + + // edit actions + Route::post('/delete/{id}','Webkul\Bulkupload\Http\Controllers\Admin\BulkUploadController@destroy')->name('bulkupload.admin.profile.delete'); + + Route::get('/edit/{id}', 'Webkul\Bulkupload\Http\Controllers\Admin\BulkUploadController@edit')->defaults('_config', [ + 'view' => 'bulkupload::admin.bulk-upload.data-flow-profile.edit' + ])->name('bulkupload.admin.profile.edit'); + + Route::post('/update/{id}', 'Webkul\Bulkupload\Http\Controllers\Admin\BulkUploadController@update')->defaults('_config', [ + 'view' => 'bulkupload::admin.bulk-upload.data-flow-profile.index' + ])->name('admin.bulk-upload.dataflow.update-profile'); + + //mass destroy + Route::post('/massdestroy', 'Webkul\Bulkupload\Http\Controllers\Admin\BulkUploadController@massDestroy')->defaults('_config', [ + 'redirect' => 'admin.dataflow-profile.index' + ])->name('bulkupload.admin.profile.massDelete'); + }); + }); + }); + }); +}); diff --git a/packages/Webkul/Bulkupload/src/Models/DataFlowProfile.php b/packages/Webkul/Bulkupload/src/Models/DataFlowProfile.php new file mode 100644 index 000000000..f452d6391 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Models/DataFlowProfile.php @@ -0,0 +1,20 @@ +hasOne(AttributeFamily::class, 'id', 'attribute_family_id'); + } +} diff --git a/packages/Webkul/Bulkupload/src/Models/DataFlowProfileProxy.php b/packages/Webkul/Bulkupload/src/Models/DataFlowProfileProxy.php new file mode 100644 index 000000000..cc7f9141f --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Models/DataFlowProfileProxy.php @@ -0,0 +1,9 @@ +app->register(ModuleServiceProvider::class); + $this->app->register(EventServiceProvider::class); + + $this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations'); + + $this->loadTranslationsFrom(__DIR__ . '/../Resources/lang', 'bulkupload'); + + $this->loadViewsFrom(__DIR__ . '/../Resources/views', 'bulkupload'); + + $this->publishes([ + __DIR__ . '/../../publishable/assets' => public_path('themes/default/assets'), + ], 'public'); + + $this->publishes([ + __DIR__ . '/../Resources/views/admin/bulk-upload/layouts/nav-aside.blade.php' => resource_path('views/vendor/admin/layouts/nav-aside.blade.php'), + ]); + + view()->composer(['bulkupload::admin.bulk-upload.upload-files.index'], function ($view) { + $items = []; + + foreach (config('product_types') as $item) { + $item['children'] = []; + + array_push($items, $item); + } + + $types = core()->sortItems($items); + + $view->with('productTypes', $types); + }); + } + + /** + * Register services. + * + * @return void + */ + public function register() + { + $this->registerConfig(); + } + + /** + * Register package config. + * + * @return void + */ + protected function registerConfig() + { + $this->mergeConfigFrom( + dirname(__DIR__) . '/Config/system.php', 'core' + ); + + $this->mergeConfigFrom( + dirname(__DIR__) . '/Config/admin-menu.php', 'menu.admin' + ); + } +} diff --git a/packages/Webkul/Bulkupload/src/Providers/EventServiceProvider.php b/packages/Webkul/Bulkupload/src/Providers/EventServiceProvider.php new file mode 100644 index 000000000..553062bfb --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Providers/EventServiceProvider.php @@ -0,0 +1,21 @@ +addTemplate('bulkupload::admin.bulk-upload.style'); + }); + } +} \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Providers/ModuleServiceProvider.php b/packages/Webkul/Bulkupload/src/Providers/ModuleServiceProvider.php new file mode 100644 index 000000000..b92eac1b8 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Providers/ModuleServiceProvider.php @@ -0,0 +1,13 @@ +productAttributeValueRepository = $productAttributeValueRepository; + + $this->productRepository = $productRepository; + + $this->productInventoryRepository = $productInventoryRepository; + + $this->productImageRepository = $productImageRepository; + + parent::__construct($app); + } + + /** + * Specify Model class name + * + * @return mixed + */ + function model() + { + return 'Webkul\Product\Contracts\Product'; + } + + /** + * update configurable product variants + * + * @param array $data + * @param integer $id + * @param string $attribute + * + * @return mixed + */ + public function productRepositoryUpdateForVariants(array $data, $id, $attribute = "id") + { + Event::dispatch('catalog.product.update.before', $id); + + $product = $this->find($id); + + $configurable = app('Webkul\Product\Type\Configurable'); + + if ($product->parent_id && $configurable->checkVariantOptionAvailabiliy($data, $product)) { + $data['parent_id'] = NULL; + } + + $product->update($data); + + $attributes = $product->attribute_family->custom_attributes; + + foreach ($attributes as $attribute) { + if (! isset($data[$attribute->code]) || (in_array($attribute->type, ['date', 'datetime']) && ! $data[$attribute->code])) + continue; + + if ($attribute->type == 'multiselect' || $attribute->type == 'checkbox') { + $data[$attribute->code] = implode(",", $data[$attribute->code]); + } + + if ($attribute->type == 'image' || $attribute->type == 'file') { + $dir = 'product'; + if (gettype($data[$attribute->code]) == 'object') { + $data[$attribute->code] = request()->file($attribute->code)->store($dir); + } else { + $data[$attribute->code] = NULL; + } + } + + $attributeValue = $this->productAttributeValueRepository->findOneWhere([ + 'product_id' => $product->id, + 'attribute_id' => $attribute->id, + 'channel' => $attribute->value_per_channel ? $data['channel'] : null, + 'locale' => $attribute->value_per_locale ? $data['locale'] : null + ]); + + if (! $attributeValue) { + $this->productAttributeValueRepository->create([ + 'product_id' => $product->id, + 'attribute_id' => $attribute->id, + 'value' => $data[$attribute->code], + 'channel' => $attribute->value_per_channel ? $data['channel'] : null, + 'locale' => $attribute->value_per_locale ? $data['locale'] : null + ]); + } else { + $this->productAttributeValueRepository->update([ + ProductAttributeValue::$attributeTypeFields[$attribute->type] => $data[$attribute->code] + ], $attributeValue->id + ); + + if ($attribute->type == 'image' || $attribute->type == 'file') { + Storage::delete($attributeValue->text_value); + } + } + } + + if (request()->route()->getName() != 'admin.catalog.products.massupdate') { + if (isset($data['categories'])) { + $product->categories()->sync($data['categories']); + } + + if (isset($data['up_sell'])) { + $product->up_sells()->sync($data['up_sell']); + } else { + $data['up_sell'] = []; + $product->up_sells()->sync($data['up_sell']); + } + + if (isset($data['cross_sell'])) { + $product->cross_sells()->sync($data['cross_sell']); + } else { + $data['cross_sell'] = []; + $product->cross_sells()->sync($data['cross_sell']); + } + + if (isset($data['related_products'])) { + $product->related_products()->sync($data['related_products']); + } else { + $data['related_products'] = []; + $product->related_products()->sync($data['related_products']); + } + + $previousVariantIds = $product->variants->pluck('id'); + if (isset($data['variants'])) { + foreach ($data['variants'] as $variantId => $variantData) { + if (str_contains($variantId, 'variant_')) { + $permutation = []; + foreach ($product->super_attributes as $superAttribute) { + $permutation[$superAttribute->id] = $variantData[$superAttribute->code]; + } + + $this->productRepository>createVariant($product, $permutation, $variantData); + } else { + if (is_numeric($index = $previousVariantIds->search($variantId))) { + $previousVariantIds->forget($index); + } + + $variantData['channel'] = $data['channel']; + $variantData['locale'] = $data['locale']; + + $this->productRepository->updateVariant($variantData, $variantId); + } + } + } + + $this->productInventoryRepository->saveInventories($data, $product); + + $this->productImageRepository->uploadImages($data, $product); + } + + if (isset($data['channels'])) { + $product['channels'] = $data['channels']; + } + + Event::dispatch('catalog.product.update.after', $product); + + return $product; + } +} \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Repositories/DataFlowProfileRepository.php b/packages/Webkul/Bulkupload/src/Repositories/DataFlowProfileRepository.php new file mode 100644 index 000000000..81f9916b7 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Repositories/DataFlowProfileRepository.php @@ -0,0 +1,22 @@ +images()->pluck('id'); + + if (isset($data['images'])) { + foreach ($data['images'] as $imageId => $image) { + $file = 'images.' . $imageId; + $dir = 'product/' . $product->id; + + if (str_contains($imageId, 'image_')) { + if (request()->hasFile($file)) { + $this->create([ + 'path' => request()->file($file)->store($dir), + 'product_id' => $product->id + ]); + } + } else { + if (is_numeric($index = $previousImageIds->search($imageId))) { + $previousImageIds->forget($index); + } + + if (request()->hasFile($file)) { + if ($imageModel = $this->find($imageId)) { + Storage::delete($imageModel->path); + } + + $this->update([ + 'path' => request()->file($file)->store($dir) + ], $imageId); + } + } + } + } + + foreach ($previousImageIds as $imageId) { + if ($imageModel = $this->find($imageId)) { + Storage::delete($imageModel->path); + + $this->delete($imageId); + } + } + } + + /** + * @param array $data + * @param mixed $product + * @param array $imageZipName + * + * @return mixed + */ + public function bulkuploadImages($data, $product, $imageZipName) + { + if (isset($data['images'])) { + foreach($data['images'] as $key => $value) { + if ( ! is_null($imageZipName)) { + $files = "imported-products/extracted-images/admin/".$data['dataFlowProfileRecordId'].'/'. $imageZipName['dirname'].'/'.basename($value); + } else { + $files = "imported-products/extracted-images/admin/".$data['dataFlowProfileRecordId'].'/'.basename($value); + } + + $destination = "product/".$product->id.'/'.basename($value); + + Storage::copy($files, $destination); + + $this->create([ + 'path' => 'product/' . $product->id .'/'. basename($value), + 'product_id' => $product->id + ]); + } + } + } +} \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Repositories/Products/BookingProductRepository.php b/packages/Webkul/Bulkupload/src/Repositories/Products/BookingProductRepository.php new file mode 100644 index 000000000..ff56602ae --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Repositories/Products/BookingProductRepository.php @@ -0,0 +1,1004 @@ +importProductRepository = $importProductRepository; + + $this->categoryRepository = $categoryRepository; + + $this->productFlatRepository = $productFlatRepository; + + $this->productRepository = $productRepository; + + $this->productImageRepository = $productImageRepository; + + $this->attributeOptionRepository = $attributeOptionRepository; + + $this->attributeFamilyRepository = $attributeFamilyRepository; + + $this->helperRepository = $helperRepository; + } + + /* + * Specify Model class name + * + * @return mixed + */ + function model() + { + return 'Webkul\Product\Contracts\Product'; + } + + /** + * create & update booking-type product + * + * @param array $requestData + * @param array $imageZipName + * + * @return mixed + */ + public function createProduct($requestData, $imageZipName) + { + try { + $dataFlowProfileRecord = $this->importProductRepository->findOneByField + ('data_flow_profile_id', $requestData['data_flow_profile_id']); + + $csvData = (new DataGridImport)->toArray($dataFlowProfileRecord->file_path)[0]; + + if ($requestData['totalNumberOfCSVRecord'] < 1000) { + $processCSVRecords = $requestData['totalNumberOfCSVRecord']/($requestData['totalNumberOfCSVRecord']/10); + } else { + $processCSVRecords = $requestData['totalNumberOfCSVRecord']/($requestData['totalNumberOfCSVRecord']/100); + } + + $uptoProcessCSVRecords = (int)$requestData['countOfStartedProfiles'] + 10; + $processRecords = (int)$requestData['countOfStartedProfiles'] + (int)$requestData['numberOfCSVRecord']; + + $slot = []; + + if ($requestData['numberOfCSVRecord'] > $processCSVRecords) { + for ($i = $requestData['countOfStartedProfiles']; $i < $uptoProcessCSVRecords; $i++) { + $invalidateProducts = $this->store($csvData[$i], $i, $dataFlowProfileRecord, $requestData, $imageZipName); + + if (isset($invalidateProducts) && !empty($invalidateProducts)) { + return $invalidateProducts; + } + } + } else if ($requestData['numberOfCSVRecord'] <= 10) { + for ($i = $requestData['countOfStartedProfiles']; $i < $processRecords; $i++) { + $invalidateProducts = $this->store($csvData[$i], $i, $dataFlowProfileRecord, $requestData, $imageZipName); + + if (isset($invalidateProducts) && !empty($invalidateProducts)) { + return $invalidateProducts; + } + } + } + + if ($requestData['numberOfCSVRecord'] > 10) { + $remainDataInCSV = (int)$requestData['numberOfCSVRecord'] - (int)$processCSVRecords; + } else { + $remainDataInCSV = 0; + + if($requestData['errorCount'] > 0) { + $uptoProcessCSVRecords = $requestData['totalNumberOfCSVRecord'] - $requestData['errorCount']; + } else { + $uptoProcessCSVRecords = $processRecords; + } + } + + $requestData['countOfStartedProfiles'] = $i; + + $dataToBeReturn = [ + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $uptoProcessCSVRecords, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + ]; + + return $dataToBeReturn; + } catch(\Exception $e) { + \Log::error('booking create product log: '. $e->getMessage()); + + $categoryError = explode('[' ,$e->getMessage()); + $categorySlugError = explode(']' ,$e->getMessage()); + $requestData['countOfStartedProfiles'] = $i + 1; + $productsUploaded = $i - $requestData['errorCount']; + + if ($requestData['numberOfCSVRecord'] != 0) { + $remainDataInCSV = (int)$requestData['totalNumberOfCSVRecord'] - (int)$requestData['countOfStartedProfiles']; + } else { + $remainDataInCSV = 0; + } + + if ($categoryError[0] == "No query results for model ") { + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => "Invalid Category Slug: " . $categorySlugError[1], + ); + $categoryError[0] = null; + } else if (isset($e->errorInfo)) { + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => $e->errorInfo[2], + ); + } else { + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => $e->getMessage(), + ); + } + + return $dataToBeReturn; + } + } + + /** + * function to store product + * + * @param array $csvData + * @param integer $i + * @param \Webkul\Bulkupload\Contracts\ImportProduct $dataFlowProfileRecord + * @param array $requestData + * @param array $imageZipName + * + * @return mixed + */ + public function store($csvData, $i, $dataFlowProfileRecord, $requestData, $imageZipName) + { + try { + $createValidation = $this->helperRepository->createProductValidation($csvData, $i); + + if (isset($createValidation)) { + return $createValidation; + } + + request()->request->add(['i' => $i]); + + $productFlatData = $this->productFlatRepository->findWhere(['sku' => $csvData['sku'], 'url_key' => $csvData['url_key']])->first(); + + $productData = $this->productRepository->findWhere(['sku' => $csvData['sku']])->first(); + + $attributeFamilyData = $this->attributeFamilyRepository->findOneByfield(['name' => $csvData['attribute_family_name']]); + + if (! isset($productFlatData) && empty($productFlatData)) { + $data['type'] = $csvData['type']; + $data['attribute_family_id'] = $attributeFamilyData->id; + $data['sku'] = $csvData['sku']; + + $bookingProductData = $this->productRepository->create($data); + } else { + $bookingProductData = $productData; + } + + unset($data); + $data = []; + $attributeCode = []; + $attributeValue = []; + + //default attributes + foreach ($bookingProductData->getTypeInstance()->getEditableAttributes()->toArray() as $key => $value) { + $searchIndex = $value['code']; + if (array_key_exists($searchIndex, $csvData)) { + if (is_null($csvData[$searchIndex])) { + continue; + } + + array_push($attributeCode, $searchIndex); + + if ($searchIndex == "color" || $searchIndex == "size" || $searchIndex == "brand") { + $attributeOption = $this->attributeOptionRepository->findOneByField(['admin_name' => ucwords($csvData[$searchIndex])]); + + array_push($attributeValue, $attributeOption['id']); + } else { + array_push($attributeValue, $csvData[$searchIndex]); + } + + $data = array_combine($attributeCode, $attributeValue); + } + } + + $data['dataFlowProfileRecordId'] = $dataFlowProfileRecord->id; + + $categoryData = explode(',', $csvData['categories_slug']); + + + if (is_null($csvData['categories_slug']) || empty($csvData['categories_slug'])) { + $categoryID = $this->categoryRepository->findBySlugOrFail('root')->id; + } else { + foreach ($categoryData as $key => $value) { + $categoryID[$key] = $this->categoryRepository->findBySlugOrFail($categoryData[$key])->id; + } + } + + $data['categories'] = $categoryID; + + $data['channel'] = core()->getCurrentChannel()->code; + + $dataProfile = app('Webkul\Bulkupload\Repositories\DataFlowProfileRepository')->findOneByfield(['id' => $data['dataFlowProfileRecordId']]); + $data['locale'] = $dataProfile->locale_code; + + //customerGroupPricing + if (isset($csvData['customer_group_prices']) && ! empty($csvData['customer_group_prices'])) { + $data['customer_group_prices'] = json_decode($csvData['customer_group_prices'], true); + app(ProductCustomerGroupPriceRepository::class)->saveCustomerGroupPrices($data, $simpleproductData); + } + + //booking product attributes + + if (strtolower($csvData['booking_type']) == "default") { + $booking = $this->defaultBookingType($csvData); + } else if (strtolower($csvData['booking_type']) == "appointment") { + $booking = $this->appointmentBookingType($csvData); + } else if (strtolower($csvData['booking_type']) == "event") { + $booking = $this->eventBookingType($csvData); + } else if (strtolower($csvData['booking_type']) == "rental") { + $booking = $this->rentalBookingType($csvData); + } else if (strtolower($csvData['booking_type']) == "table") { + $booking = $this->tableBookingType($csvData); + } else { + \Log::error('booking type not found'); + } + + $data['booking'] = $booking; + + //images + $individualProductimages = explode(',', $csvData['images']); + + if (isset($imageZipName)) { + $images = Storage::disk('local')->files('public/imported-products/extracted-images/admin/'.$dataFlowProfileRecord->id.'/'.$imageZipName['dirname'].'/'); + + foreach ($images as $imageArraykey => $imagePath) { + $imageName = explode('/', $imagePath); + + if (in_array(last($imageName), preg_replace('/[\'"]/', '',$individualProductimages))) { + $data['images'][$imageArraykey] = $imagePath; + } + } + } else if (isset($csvData['images'])) { + foreach ($individualProductimages as $imageArraykey => $imageURL) + { + if (filter_var(trim($imageURL), FILTER_VALIDATE_URL)) { + $imagePath = storage_path('app/public/imported-products/extracted-images/admin/'.$dataFlowProfileRecord->id); + + if (!file_exists($imagePath)) { + mkdir($imagePath, 0777, true); + } + + $imageFile = $imagePath.'/'.basename($imageURL); + + file_put_contents($imageFile, file_get_contents(trim($imageURL))); + + $data['images'][$imageArraykey] = $imageFile; + } + } + } + + // to check validation + $validationRules = $this->helperRepository->validateCSV($requestData['data_flow_profile_id'], $data, $dataFlowProfileRecord, $bookingProductData); + + $csvValidator = Validator::make($data, $validationRules); + + if ($csvValidator->fails()) { + $errors = $csvValidator->errors()->getMessages(); + + $this->helperRepository->deleteProductIfNotValidated($bookingProductData->id); + + foreach($errors as $key => $error) { + if ($error[0] == "The url key has already been taken.") { + $errorToBeReturn[] = "The url key " . $data['url_key'] . " has already been taken"; + } else { + $errorToBeReturn[] = str_replace(".", "", $error[0]). " for sku " . $data['sku']; + } + } + + $requestData['countOfStartedProfiles'] = $i + 1; + + $productsUploaded = $i - $requestData['errorCount']; + + if ($requestData['numberOfCSVRecord'] != 0) { + $remainDataInCSV = (int)$requestData['totalNumberOfCSVRecord'] - (int)$requestData['countOfStartedProfiles']; + } else { + $remainDataInCSV = 0; + } + + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => $errorToBeReturn, + ); + + return $dataToBeReturn; + } + + request()->request->add(['booking' => $booking]); + + $this->productRepository->update($data, $bookingProductData->id); + + if (isset($imageZipName)) { + $this->productImageRepository->bulkuploadImages($data, $bookingProductData, $imageZipName); + } else if (isset($csvData['images'])) { + $this->productImageRepository->bulkuploadImages($data, $bookingProductData, $imageZipName = null); + } + } catch(\Exception $e) { + \Log::error('booking product store function'. $e->getMessage()); + } + } + + /** + * prepare data for default booking-type + * + * @param array $data + * + * @return mixed + */ + public function defaultBookingType($data) + { + try { + if (isset($data['booking_slot_from']) && !empty($data['booking_slot_from'])) { + $slot = $this->prepareDefaultBookingSlots($data); + } + + if (strtolower($data['booking_type_day_wise']) == "many") { + $bookingType = "many"; + $duration = $data['duration'] ?? 45; + $breakTime = $data['break_time'] ?? 15; + } else { + $bookingType = "one"; + } + + $availableFrom = explode(',', $data['booking_available_from']); + + foreach ($availableFrom as $key => $availableDateTime) { + $dateFormat = str_replace('/', '-', $availableFrom["0"]); + $date = date('Y-m-d', strtotime($dateFormat)); + + $time = date("H:i:s", strtotime($availableFrom["1"])); + + $from = $date.' '.$time; + } + + $availableTo = explode(',' , $data['booking_available_to']); + + foreach ($availableTo as $key => $availableDateTime) { + $dateFormat = str_replace('/', '-', $availableTo["0"]); + $date = date('Y-m-d', strtotime($dateFormat)); + + $time = date("H:i:s", strtotime($availableTo["1"])); + + $to = $date.' '.$time; + } + + $booking = [ + "type" => "default", + "location" => $data['booking_location'], + "qty" => $data['booking_qty'] ?? 0, + "available_from" => $from, + "available_to" => $to, + "booking_type" => $bookingType, + "duration" => $duration ?? "", + "break_time" => $breakTime ?? "" + ]; + + if (! empty($slot)) { + $booking['slots'] = $slot; + } + + return $booking; + } catch (\Exception $e) { + \Log::error('booking defaultBookingType log: '. $e->getMessage()); + } + } + + /** + * prepare data for appointment booking-type + * + * @param array $data + * + * @return mixed + */ + public function appointmentBookingType($data) + { + try { + if (isset($data['booking_slot_from']) && !empty($data['booking_slot_from'])) { + $slot = $this->prepareAppointmentBookingSlots($data); + } + + if ($data['available_every_week'] == "yes") { + $appointment = [ + "type" => "appointment", + "location" => $data['booking_location'], + "qty" => $data['booking_qty'] ?? 0, + "available_every_week" => "1", + "duration" => $data['duration'] ?? 45, + "break_time" => $data['break_time'] ?? 15, + "same_slot_all_days" => $data['same_slot_all_days'] ? 1 : 0 + ]; + } else { + $availableFrom = explode(',', $data['booking_available_from']); + + foreach ($availableFrom as $key => $availableDateTime) { + $dateFormat = str_replace('/', '-', $availableFrom["0"]); + $date = date('Y-m-d', strtotime($dateFormat)); + + $time = date("H:i:s", strtotime($availableFrom["1"])); + + $from = $date.' '.$time; + } + + $availableTo = explode(',' , $data['booking_available_to']); + + foreach ($availableTo as $key => $availableDateTime) { + $dateFormat = str_replace('/', '-', $availableTo["0"]); + $date = date('Y-m-d', strtotime($dateFormat)); + + $time = date("H:i:s", strtotime($availableTo["1"])); + + $to = $date.' '.$time; + } + + $appointment = [ + "type" => "appointment", + "location" => $data['booking_location'], + "qty" => $data['booking_qty'] ?? 0, + "available_every_week" => "0", + "available_from" => $from, + "available_to" => $to, + "duration" => $data['duration'], + "break_time" => $data['break_time'], + "same_slot_all_days" => $data['same_slot_all_days'] ? 1 : 0 + ]; + } + + if (! empty($slot)) { + $appointment['slots'] = $slot; + } + + return $appointment; + } catch (\Exception $e) { + \Log::error('booking appointmentBookingType log: '. $e->getMessage()); + } + } + + /** + * prepare data for event booking-type + * + * @param array $data + * + * @return mixed + */ + public function eventBookingType($data) + { + try { + if (isset($data['booking_available_from']) && !empty($data['booking_available_to'])) { + $ticket = $this->prepareEventBookingTickets($data); + } + + $availableFrom = explode(',', $data['booking_available_from']); + + foreach ($availableFrom as $key => $availableDateTime) { + $dateFormat = str_replace('/', '-', $availableFrom["0"]); + $date = date('Y-m-d', strtotime($dateFormat)); + + $time = date("H:i:s", strtotime($availableFrom["1"])); + + $from = $date.' '.$time; + } + + $availableTo = explode(',' , $data['booking_available_to']); + + foreach ($availableTo as $key => $availableDateTime) { + $dateFormat = str_replace('/', '-', $availableTo["0"]); + $date = date('Y-m-d', strtotime($dateFormat)); + + $time = date("H:i:s", strtotime($availableTo["1"])); + + $to = $date.' '.$time; + } + + $booking = [ + "type" => "event", + "location" => $data['booking_location'], + "available_from" => $from, + "available_to" => $to, + ]; + + if (! empty($ticket)) { + $booking['tickets'] = $ticket; + } + + return $booking; + } catch (\Exception $e) { + \Log::error('booking eventBookingType log: '. $e->getMessage()); + } + } + + /** + * prepare data for rental booking-type + * + * @param array $data + * + * @return mixed + */ + public function rentalBookingType($data) + { + try { + if ($data['available_every_week'] == "no" || empty($data['available_every_week'])) { + $availableEveryWeek = "0"; + + $availableFrom = explode(',', $data['booking_available_from']); + + foreach ($availableFrom as $key => $availableDateTime) { + $dateFormat = str_replace('/', '-', $availableFrom["0"]); + $date = date('Y-m-d', strtotime($dateFormat)); + + $time = date("H:i:s", strtotime($availableFrom["1"])); + + $from = $date.' '.$time; + } + + $availableTo = explode(',' , $data['booking_available_to']); + + foreach ($availableTo as $key => $availableDateTime) { + $dateFormat = str_replace('/', '-', $availableTo["0"]); + $date = date('Y-m-d', strtotime($dateFormat)); + + $time = date("H:i:s", strtotime($availableTo["1"])); + + $to = $date.' '.$time; + } + + $booking['booking_available_from'] = $from; + $booking['booking_available_to'] = $to; + } else { + $availableEveryWeek = "1"; + } + + if ($data['renting_type'] == "daily") { + $booking = [ + "type" => "rental", + "location" => $data['booking_location'], + "qty" => $data['booking_qty'], + "available_every_week" => $availableEveryWeek, + "renting_type" => "daily", + "daily_price" => $data['daily_price'] ?? 0, + ]; + } else if ($data['renting_type'] == "hourly") { + $slot = $this->prepareRentalBookingSlots($data); + + $booking = [ + "type" => "rental", + "location" => $data['booking_location'], + "qty" => $data['booking_qty'], + "available_every_week" => $availableEveryWeek, + "renting_type" => "hourly", + "hourly_price" => $data['hourly_price'] ?? 0, + "same_slot_all_days" => $data['same_slot_all_days'] + ]; + + if (! empty($slot)) { + $booking['slots'] = $slot; + } + } else if ($data['renting_type'] == "daily_hourly") { + $slot = $this->prepareRentalBookingSlots($data); + + if (trim(strtolower($data['same_slot_all_days'])) == "no" || empty($data['same_slot_all_days'])) { + $data['same_slot_all_days'] = null; + } + + $booking = [ + "type" => "rental", + "location" => $data['booking_location'], + "qty" => $data['booking_qty'], + "available_every_week" => $availableEveryWeek, + "renting_type" => "daily_hourly", + "daily_price" => $data['daily_price'] ?? 0, + "hourly_price" => $data['hourly_price'] ?? 0, + "same_slot_all_days" => $data['same_slot_all_days'] ? 1 : 0 + ]; + + if (! empty($slot)) { + $booking['slots'] = $slot; + } + } + + return $booking; + } catch (\Exception $e) { + \Log::error('booking rentalBookingType log: '. $e->getMessage()); + } + } + + /** + * prepare data for table booking-type + * + * @param array $data + * + * @return mixed + */ + public function tableBookingType($data) + { + try { + if ($data['available_every_week'] == "no" || empty($data['available_every_week'])) { + $availableEveryWeek = "0"; + + $availableFrom = explode(',', $data['booking_available_from']); + + foreach ($availableFrom as $key => $availableDateTime) { + $dateFormat = str_replace('/', '-', $availableFrom["0"]); + $date = date('Y-m-d', strtotime($dateFormat)); + + $time = date("H:i:s", strtotime($availableFrom["1"])); + + $from = $date.' '.$time; + } + + $availableTo = explode(',' , $data['booking_available_to']); + + foreach ($availableTo as $key => $availableDateTime) { + $dateFormat = str_replace('/', '-', $availableTo["0"]); + $date = date('Y-m-d', strtotime($dateFormat)); + + $time = date("H:i:s", strtotime($availableTo["1"])); + + $to = $date.' '.$time; + } + + $booking['booking_available_from'] = $from; + $booking['booking_available_to'] = $to; + } else { + $availableEveryWeek = "1"; + } + + if ($data['price_type'] == "table") { + $booking['guest_limit'] = $data['guest_limit'] ?? 0; + } + + if (trim(strtolower($data['same_slot_all_days'])) == "no" || empty($data['same_slot_all_days'])) { + $data['same_slot_all_days'] = null; + } + + $slot = $this->prepareRentalBookingSlots($data); + + $booking = [ + "type" => "table", + "location" => $data['booking_location'], + "qty" => $data['booking_qty'], + "available_every_week" => $availableEveryWeek, + "price_type" => $data['price_type'], + "duration" => $data['duration'], + "break_time" => $data['break_time'], + "prevent_scheduling_before" => $data['prevent_scheduling_before'], + "same_slot_all_days" => $data['same_slot_all_days'] ? 1 : 0 + ]; + + if (! empty($slot)) { + $booking['slots'] = $slot; + } + + return $booking; + } catch (\Exception $e) { + \Log::error('booking tableBookingType log: '. $e->getMessage()); + } + } + + /** + * prepare slots for default booking type + * + * @param array $data + * + * @return array + */ + public function prepareDefaultBookingSlots($record) + { + try { + $weekNames = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'); + + $bookingSlotFromDay = explode(',', $record['booking_slot_from_day']); + $bookingSlotFrom = explode(',', $record['booking_slot_from']) ?? "12:00:00"; + $bookingSlotToDay = explode(',', $record['booking_slot_to_day']); + $bookingSlotTo = explode(',', $record['booking_slot_to']) ?? "13:00:00"; + + if (strtolower($record['booking_type_day_wise']) == "one") { + for ($j = 0; $j < count($bookingSlotFrom); $j++) { + $fromDay = array_search(strtolower($bookingSlotFromDay[$j]), $weekNames); + $toDay = array_search(strtolower($bookingSlotToDay[$j]), $weekNames); + + $from = date("H:i:s", strtotime($bookingSlotFrom[$j])); + $to = date("H:i:s", strtotime($bookingSlotTo[$j])); + + $slotter[$j] = [ + "from_day" => $fromDay, + "from" => $from, + "to_day" => $toDay, + "to" => $to, + ]; + } + } else { + $defaultBookingStatus = explode(',', $record['default_booking_status']); + + for ($j = 0; $j < count($weekNames); $j++) { + $from = date("H:i:s", strtotime($bookingSlotFrom[$j])); + $to = date("H:i:s", strtotime($bookingSlotTo[$j])); + + $slotter[$j] = [ + "from" => $from, + "to" => $to, + "status" => $defaultBookingStatus[$j] ?? 0 + ]; + } + } + + return $slotter; + } catch(\Exception $e) { + \Log::error('booking prepareDefaultBookingSlots log: '. $e->getMessage()); + } + } + + /** + * prepare slots for appointment booking type + * + * @param array $data + * + * @return array + */ + public function prepareAppointmentBookingSlots($record) + { + try { + $bookingSlotFrom = explode(',', $record['booking_slot_from']); + $bookingSlotTo = explode(',', $record['booking_slot_to']); + + for($j = 0; $j < count($bookingSlotFrom); $j++) { + $from = date("H:i:s", strtotime($bookingSlotFrom[$j])); + $to = date("H:i:s", strtotime($bookingSlotTo[$j])); + + if ($record['same_slot_all_days'] == "no" || empty($record['same_slot_all_days'])) { + $weekNames = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'); + + $dayWiseSlots = explode(',', $record['day_wise_slots']); + + $weekDayNumber = array_search(trim(strtolower($dayWiseSlots[$j])), $weekNames); + $slotter[$weekDayNumber] = [ + $slotArray[$j] = [ + "from" => $from, + "to" => $to, + ] + ]; + } else { + $slotter[$j] = [ + "from" => $from, + "to" => $to, + ]; + } + } + + return $slotter; + } catch (\Exception $e) { + \Log::error('booking prepareAppointmentBookingSlots log: '. $e->getMessage()); + } + } + + /** + * prepare tickets for event booking type + * + * @param array $data + * + * @return array + */ + public function prepareEventBookingTickets($record) + { + try { + $ticketName = explode(',', $record['event_ticket_name']); + $ticketDescription = explode(',', $record['event_ticket_description']); + $ticketPrice = explode(',', $record['event_ticket_price']); + $ticketQty = explode(',', $record['event_ticket_qty']); + + for($j = 0; $j < count($ticketName); $j++) { + $slotter['ticket_'.$j] = [ + core()->getCurrentLocale()->code => [ + "name" => $ticketName[$j], + "description" => $ticketDescription[$j] + ], + "price" => $ticketPrice[$j], + "qty" => $ticketQty[$j], + ]; + } + + return $slotter; + } catch (\Exception $e) { + \Log::error('booking prepareEventBookingTickets log: '. $e->getMessage()); + } + } + + /** + * prepare slots for rental booking type + * + * @param array $data + * + * @return array + */ + public function prepareRentalBookingSlots($record) + { + try { + $bookingSlotFrom = explode(',', $record['booking_slot_from']); + $bookingSlotTo = explode(',', $record['booking_slot_to']); + $weekDayNumber = []; + $slotter = []; + + for($j = 0; $j < count($bookingSlotFrom); $j++) { + $from = date("H:i:s", strtotime($bookingSlotFrom[$j])); + $to = date("H:i:s", strtotime($bookingSlotTo[$j])); + + if ($record['same_slot_all_days'] == "no" || empty($record['same_slot_all_days'])) { + $weekNames = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'); + + $dayWiseSlots = explode(',', $record['day_wise_slots']); + + $weekDayNumber = array_search(trim(strtolower($dayWiseSlots[$j])), $weekNames); + $slotter[$weekDayNumber] = [ + $slotArray[$j] = [ + "from" => $from, + "to" => $to, + ] + ]; + } else { + $slotter[$j] = [ + "from" => $from, + "to" => $to, + ]; + } + } + + return $slotter; + } catch (\Exception $e) { + \Log::error('booking prepareRentalBookingSlots log: '. $e->getMessage()); + } + } + + /** + * prepare slots for table booking type + * + * @param array $data + * + * @return array + */ + public function prepareTableBookingSlots($record) + { + try { + $bookingSlotFrom = explode(',', $record['booking_slot_from']); + $bookingSlotTo = explode(',', $record['booking_slot_to']); + $weekDayNumber = []; + $slotter = []; + + for($j = 0; $j < count($bookingSlotFrom); $j++) { + $from = date("H:i:s", strtotime($bookingSlotFrom[$j])); + $to = date("H:i:s", strtotime($bookingSlotTo[$j])); + + if ($record['same_slot_all_days'] == "no" || empty($record['same_slot_all_days'])) { + $weekNames = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'); + + $dayWiseSlots = explode(',', $record['day_wise_slots']); + + $weekDayNumber = array_search(trim(strtolower($dayWiseSlots[$j])), $weekNames); + $slotter[$weekDayNumber] = [ + $slotArray[$j] = [ + "from" => $from, + "to" => $to, + ] + ]; + } else { + $slotter[$j] = [ + "from" => $from, + "to" => $to, + ]; + } + } + + return $slotter; + } catch (\Exception $e) { + \Log::error('booking prepareTableBookingSlots log: '. $e->getMessage()); + } + } +} \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Repositories/Products/BundledProductRepository.php b/packages/Webkul/Bulkupload/src/Repositories/Products/BundledProductRepository.php new file mode 100644 index 000000000..48ab5d5fb --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Repositories/Products/BundledProductRepository.php @@ -0,0 +1,404 @@ +importProductRepository = $importProductRepository; + + $this->categoryRepository = $categoryRepository; + + $this->productFlatRepository = $productFlatRepository; + + $this->productRepository = $productRepository; + + $this->productImageRepository = $productImageRepository; + + $this->attributeFamilyRepository = $attributeFamilyRepository; + + $this->helperRepository = $helperRepository; + + $this->attributeOptionRepository = $attributeOptionRepository; + } + + /* + * Specify Model class name + * + * @return mixed + */ + function model() + { + return 'Webkul\Product\Contracts\Product'; + } + + /** + * create & update bundled-type product + * + * @param array $requestData + * @param array $imageZipName + * + * @return mixed + */ + public function createProduct($requestData, $imageZipName) + { + try { + $dataFlowProfileRecord = $this->importProductRepository->findOneByField + ('data_flow_profile_id', $requestData['data_flow_profile_id']); + + $csvData = (new DataGridImport)->toArray($dataFlowProfileRecord->file_path)[0]; + + if ($requestData['totalNumberOfCSVRecord'] < 1000) { + $processCSVRecords = $requestData['totalNumberOfCSVRecord']/($requestData['totalNumberOfCSVRecord']/10); + } else { + $processCSVRecords = $requestData['totalNumberOfCSVRecord']/($requestData['totalNumberOfCSVRecord']/100); + } + + $uptoProcessCSVRecords = (int)$requestData['countOfStartedProfiles'] + 10; + $processRecords = (int)$requestData['countOfStartedProfiles'] + (int)$requestData['numberOfCSVRecord']; + + if ($requestData['numberOfCSVRecord'] > $processCSVRecords) { + for ($i = $requestData['countOfStartedProfiles']; $i < $uptoProcessCSVRecords; $i++) { + $invalidateProducts = $this->store($csvData[$i], $i, $dataFlowProfileRecord, $requestData, $imageZipName); + + if (isset($invalidateProducts) && !empty($invalidateProducts)) { + return $invalidateProducts; + } + } + } else if ($requestData['numberOfCSVRecord'] <= 10) { + for ($i = $requestData['countOfStartedProfiles']; $i < $processRecords; $i++) { + $invalidateProducts = $this->store($csvData[$i], $i, $dataFlowProfileRecord, $requestData, $imageZipName); + + if (isset($invalidateProducts) && !empty($invalidateProducts)) { + return $invalidateProducts; + } + } + } + + if ($requestData['numberOfCSVRecord'] > 10) { + $remainDataInCSV = (int)$requestData['numberOfCSVRecord'] - (int)$processCSVRecords; + } else { + $remainDataInCSV = 0; + + if($requestData['errorCount'] > 0) { + $uptoProcessCSVRecords = $requestData['totalNumberOfCSVRecord'] - $requestData['errorCount']; + } else { + $uptoProcessCSVRecords = $processRecords; + } + } + + $requestData['countOfStartedProfiles'] = $i; + + $dataToBeReturn = [ + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $uptoProcessCSVRecords, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + ]; + + return $dataToBeReturn; + } catch(\Exception $e) { + $categoryError = explode('[' ,$e->getMessage()); + $categorySlugError = explode(']' ,$e->getMessage()); + $requestData['countOfStartedProfiles'] = $i + 1; + $productsUploaded = $i - $requestData['errorCount']; + + if ($requestData['numberOfCSVRecord'] != 0) { + $remainDataInCSV = (int)$requestData['totalNumberOfCSVRecord'] - (int)$requestData['countOfStartedProfiles']; + } else { + $remainDataInCSV = 0; + } + + if ($categoryError[0] == "No query results for model ") { + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => "Invalid Category Slug: " . $categorySlugError[1], + ); + $categoryError[0] = null; + } else if (isset($e->errorInfo)) { + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => $e->errorInfo[2], + ); + } else { + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => $e->getMessage(), + ); + } + + return $dataToBeReturn; + } + } + + /** + * function to store product + * + * @param array $csvData + * @param integer $i + * @param \Webkul\Bulkupload\Contracts\ImportProduct $dataFlowProfileRecord + * @param array $requestData + * @param array $imageZipName + * + * @return mixed + */ + public function store($csvData, $i, $dataFlowProfileRecord, $requestData, $imageZipName) + { + $createValidation = $this->helperRepository->createProductValidation($csvData, $i); + + if (isset($createValidation)) { + return $createValidation; + } + + $productFlatData = $this->productFlatRepository->findWhere(['sku' => $csvData['sku'], 'url_key' => $csvData['url_key']])->first(); + + $productData = $this->productRepository->findWhere(['sku' => $csvData['sku']])->first(); + + $attributeFamilyData = $this->attributeFamilyRepository->findOneByfield(['name' => $csvData['attribute_family_name']]); + + if (! isset($productFlatData) && empty($productFlatData)) { + $data['type'] = $csvData['type']; + $data['attribute_family_id'] = $attributeFamilyData->id; + $data['sku'] = $csvData['sku']; + + $bundledProduct = $this->productRepository->create($data); + } else { + $bundledProduct = $productData; + } + + unset($data); + + $data = []; + $attributeCode = []; + $attributeValue = []; + + //default attributes + foreach ($bundledProduct->getTypeInstance()->getEditableAttributes()->toArray() as $key => $value) { + $searchIndex = $value['code']; + + if (array_key_exists($searchIndex, $csvData)) { + if (is_null($csvData[$searchIndex])) { + continue; + } + + array_push($attributeCode, $searchIndex); + + if ($searchIndex == "color" || $searchIndex == "size" || $searchIndex == "brand") { + $attributeOption = $this->attributeOptionRepository->findOneByField(['admin_name' => ucwords($csvData[$searchIndex])]); + + array_push($attributeValue, $attributeOption['id']); + } else { + array_push($attributeValue, $csvData[$searchIndex]); + } + + $data = array_combine($attributeCode, $attributeValue); + } + } + + $data['dataFlowProfileRecordId'] = $dataFlowProfileRecord->id; + + $categoryData = explode(',', $csvData['categories_slug']); + + if (is_null($csvData['categories_slug']) || empty($csvData['categories_slug'])) { + $categoryID = $this->categoryRepository->findBySlugOrFail('root')->id; + } else { + foreach ($categoryData as $key => $value) { + $categoryID[$key] = $this->categoryRepository->findBySlugOrFail($categoryData[$key])->id; + } + } + + $data['categories'] = $categoryID; + $data['channel'] = core()->getCurrentChannel()->code; + + $dataProfile = app('Webkul\Bulkupload\Repositories\DataFlowProfileRepository')->findOneByfield(['id' => $data['dataFlowProfileRecordId']]); + $data['locale'] = $dataProfile->locale_code; + + //customerGroupPricing + if (isset($csvData['customer_group_prices']) && ! empty($csvData['customer_group_prices'])) { + $data['customer_group_prices'] = json_decode($csvData['customer_group_prices'], true); + app(ProductCustomerGroupPriceRepository::class)->saveCustomerGroupPrices($data, $simpleproductData); + } + + //prepare bundle options + $bundleOptions = json_decode($csvData['bundle_options'], true); + + $data['bundle_options'] = $bundleOptions; + + //Product Images + $individualProductimages = explode(',', $csvData['images']); + + if (isset($imageZipName)) { + $images = Storage::disk('local')->files('public/imported-products/extracted-images/admin/'.$dataFlowProfileRecord->id.'/'.$imageZipName['dirname'].'/'); + + foreach ($images as $imageArraykey => $imagePath) { + $imageName = explode('/', $imagePath); + + if (in_array(last($imageName), preg_replace('/[\'"]/', '',$individualProductimages))) { + $data['images'][$imageArraykey] = $imagePath; + } + } + } else if (isset($csvData['images'])) { + foreach ($individualProductimages as $imageArraykey => $imageURL) + { + if (filter_var(trim($imageURL), FILTER_VALIDATE_URL)) { + $imagePath = storage_path('app/public/imported-products/extracted-images/admin/'.$dataFlowProfileRecord->id); + + if (!file_exists($imagePath)) { + mkdir($imagePath, 0777, true); + } + + $imageFile = $imagePath.'/'.basename($imageURL); + + file_put_contents($imageFile, file_get_contents(trim($imageURL))); + + $data['images'][$imageArraykey] = $imageFile; + } + } + } + + $validationRules = $this->helperRepository->validateCSV($requestData['data_flow_profile_id'], $data, $dataFlowProfileRecord, $bundledProduct); + + $csvValidator = Validator::make($data, $validationRules); + + if ($csvValidator->fails()) { + $errors = $csvValidator->errors()->getMessages(); + + $this->helperRepository->deleteProductIfNotValidated($bundledProduct->id); + + foreach($errors as $key => $error) { + if ($error[0] == "The url key has already been taken.") { + $errorToBeReturn[] = "The url key " . $data['url_key'] . " has already been taken"; + } else { + $errorToBeReturn[] = str_replace(".", "", $error[0]). " for sku " . $data['sku']; + } + } + + $requestData['countOfStartedProfiles'] = $i + 1; + + $productsUploaded = $i - $requestData['errorCount']; + + if ($requestData['numberOfCSVRecord'] != 0) { + $remainDataInCSV = (int)$requestData['totalNumberOfCSVRecord'] - (int)$requestData['countOfStartedProfiles']; + } else { + $remainDataInCSV = 0; + } + + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => $errorToBeReturn, + ); + + return $dataToBeReturn; + } + + $this->productRepository->update($data, $bundledProduct->id); + + if (isset($imageZipName)) { + $this->productImageRepository->bulkuploadImages($data, $bundledProduct, $imageZipName); + } else if (isset($csvData['images'])) { + $this->productImageRepository->bulkuploadImages($data, $bundledProduct, $imageZipName = null); + } + } +} \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Repositories/Products/ConfigurableProductRepository.php b/packages/Webkul/Bulkupload/src/Repositories/Products/ConfigurableProductRepository.php new file mode 100644 index 000000000..40a1027d6 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Repositories/Products/ConfigurableProductRepository.php @@ -0,0 +1,742 @@ +importProductRepository = $importProductRepository; + + $this->categoryRepository = $categoryRepository; + + $this->productFlatRepository = $productFlatRepository; + + $this->productRepository = $productRepository; + + $this->productImageRepository = $productImageRepository; + + $this->attributeFamilyRepository = $attributeFamilyRepository; + + $this->attributeRepository = $attributeRepository; + + $this->productInventoryRepository = $productInventoryRepository; + + $this->helperRepository = $helperRepository; + + $this->bulkProductRepository = $bulkProductRepository; + + $this->attributeOptionRepository = $attributeOptionRepository; + } + + /* + * Specify Model class name + * + * @return mixed + */ + function model() + { + return 'Webkul\Product\Contracts\Product'; + } + + /** + * create & update configurable-type product + * + * @param array $requestData + * @param array $imageZipName + * @param array $product + * + * @return mixed + */ + public function createProduct($requestData, $imageZipName, $product) + { + try { + if ($requestData['totalNumberOfCSVRecord'] < 1000) { + $processCSVRecords = $requestData['totalNumberOfCSVRecord']/($requestData['totalNumberOfCSVRecord']/10); + } else { + $processCSVRecords = $requestData['totalNumberOfCSVRecord']/($requestData['totalNumberOfCSVRecord']/100); + } + + $dataFlowProfileRecord = $this->importProductRepository->findOneByField + ('data_flow_profile_id', $requestData['data_flow_profile_id']); + + if ($dataFlowProfileRecord) { + $csvData = (new DataGridImport)->toArray($dataFlowProfileRecord->file_path)[0]; + + foreach ($csvData as $key => $value) { + if ($requestData['numberOfCSVRecord'] >= 0) { + for ($i = $requestData['countOfStartedProfiles']; $i < count($csvData); $i++) { + $product['loopCount'] = $i; + + if ($csvData[$i]['type'] == 'configurable') { + try { + $createValidation = $this->helperRepository->createProductValidation($csvData[$i], $i); + + if ( isset($createValidation)) { + return $createValidation; + } + + unset($data); + + $productFlatData = $this->productFlatRepository->findOneWhere([ + 'sku' => $csvData[$i]['sku'], + 'url_key' => $csvData[$i]['url_key'] + ]); + + $productData = $this->productRepository->findOneWhere([ + 'sku' => $csvData[$i]['sku'] + ]); + + $attributeFamilyData = $this->attributeFamilyRepository->findOneByfield('name', $csvData[$i]['attribute_family_name']); + + if (! isset($productFlatData) && empty($productData)) { + $data['type'] = $csvData[$i]['type']; + $data['attribute_family_id'] = $attributeFamilyData->id; + $data['sku'] = $csvData[$i]['sku']; + + $product = $this->bulkProductRepository->create($data); + } else { + $product = $productData; + } + + unset($data); + $data = []; + $attributeCode = []; + $attributeValue = []; + + foreach ($product->getTypeInstance()->getEditableAttributes()->toArray() as $key => $value) { + $attributeOptionArray = []; + $searchIndex = strtolower($value['code']); + + if (array_key_exists($searchIndex, $csvData[$i])) { + if ($searchIndex == 'tax_category_id') { + continue; + } + + array_push($attributeCode, $searchIndex); + + if ($value['type'] == "select") { + $attributeOption = $this->attributeOptionRepository->findOneByField('admin_name', $csvData[$i][$searchIndex]); + + array_push($attributeValue, (isset($attributeOption['id']) ? $attributeOption['id'] : null)); + + } else if ($value['type'] == "checkbox") { + $attributeOption = $this->attributeOptionRepository->findOneWhere([ + 'attribute_id' => $value['id'], + 'admin_name' => $csvData[$i][$searchIndex] + ]); + + array_push($attributeOptionArray, (isset($attributeOption['id']) ? $attributeOption['id'] : null)); + + array_push($attributeValue, $attributeOptionArray); + + unset($attributeOptionArray); + } else { + array_push($attributeValue, $csvData[$i][$searchIndex]); + } + + $data = array_combine($attributeCode, $attributeValue); + } + } + + $data['dataFlowProfileRecordId'] = $dataFlowProfileRecord->id; + $data['channel'] = core()->getCurrentChannel()->code; + + $dataProfile = app('Webkul\Bulkupload\Repositories\DataFlowProfileRepository')->findOneByfield(['id' => $data['dataFlowProfileRecordId']]); + $data['locale'] = $dataProfile->locale_code; + + $data['tax_category_id'] = (isset($csvData[$i]['tax_category_id']) && $csvData[$i]['tax_category_id']) ? $csvData[$i]['tax_category_id'] : null; + + $categoryData = explode(',', $csvData[$i]['categories_slug']); + + if (is_null($csvData[$i]['categories_slug']) || empty($csvData[$i]['categories_slug'])) { + $categoryID = $this->categoryRepository->findBySlugOrFail('root')->id; + } else { + foreach ($categoryData as $key => $value) { + $categoryID[$key] = $this->categoryRepository->findBySlugOrFail($categoryData[$key])->id; + } + } + + $data['categories'] = $categoryID; + + $individualProductimages = explode(',', $csvData[$i]['images']); + + if (isset($imageZipName)) { + $images = Storage::disk('local')->files('public/imported-products/extracted-images/admin/'.$dataFlowProfileRecord->id.'/'.$imageZipName['dirname'].'/'); + + foreach ($images as $imageArraykey => $imagePath) { + $imageName = explode('/', $imagePath); + + if (in_array(last($imageName), preg_replace('/[\'"]/', '',$individualProductimages))) { + $data['images'][$imageArraykey] = $imagePath; + } + } + } else if (isset($csvData['images'])) { + foreach ($individualProductimages as $imageArraykey => $imageURL) + { + if (filter_var(trim($imageURL), FILTER_VALIDATE_URL)) { + $imagePath = storage_path('app/public/ imported-products/extracted-images/admin/'. $dataFlowProfileRecord->id); + + if (!file_exists($imagePath)) { + mkdir($imagePath, 0777, true); + } + + $imageFile = $imagePath.'/'.basename($imageURL) ; + + file_put_contents($imageFile, file_get_contents (trim($imageURL))); + + $data['images'][$imageArraykey] = $imageFile; + } + } + } + + $productAttributeStore = $this->bulkProductRepository->productRepositoryUpdateForVariants($data, $product->id); + + if (isset($imageZipName)) { + $this->productImageRepository->bulkuploadImages($data, $product, $imageZipName); + } else if (isset($csvData['images'])) { + $this->productImageRepository->bulkuploadImages($data, $product, $imageZipName = null); + } + + if (! isset($productFlatData) && empty($productFlatData)) { + $productFlatData = DB::table('product_flat')->select('id')->orderBy('id', 'desc')->first(); + } + + $product['productFlatId'] = $productFlatData->id; + + $arr[] = $productFlatData->id; + + unset($categoryID); + } catch (\Exception $e) { + $categoryError = explode('[' ,$e->getMessage()); + $categorySlugError = explode(']' ,$e->getMessage()); + + $error = $e; + + $productUploadedWithError = $requestData['productUploaded'] + 1; + $remainDataInCSV = $requestData['totalNumberOfCSVRecord'] - $productUploadedWithError; + $requestData['countOfStartedProfiles'] = $i + 1; + + if ($categoryError[0] == "No query results for model ") { + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $requestData['productUploaded'], + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => "Invalid Category Slug: " . $categorySlugError[1], + ); + $categoryError[0] = null; + } else if (isset($e->errorInfo)) { + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $requestData['productUploaded'], + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => $e->errorInfo[2], + ); + } else { + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $requestData['productUploaded'], + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => $e->getMessage(), + ); + } + return $dataToBeReturn; + } + } else if (isset($product['productFlatId'])) { + try { + $current = $product['loopCount']; + $num = 0; + $inventory = []; + + $csvData = (new DataGridImport)->toArray($dataFlowProfileRecord->file_path)[0]; + + for ($i = $current; $i < count($csvData); $i++) { + $product['loopCount'] = $i; + + if ($csvData[$i]['type'] != 'configurable') { + $productFlatData = $this->productFlatRepository->findOneWhere([ + 'sku' => $csvData[$i]['sku'], + 'url_key' => null + ]); + + $productData = $this->productRepository->findOneWhere([ + 'sku' => $csvData[$i]['sku'] + ]); + + $attributeFamilyData = $this->attributeFamilyRepository->findOneWhere([ + 'name' => $csvData[$i]['attribute_family_name'] + ]); + + if (! isset($productFlatData) && empty($productData)) { + $data['parent_id'] = $product->id; + $data['type'] = "simple"; + $data['attribute_family_id'] = $attributeFamilyData->id; + $data['sku'] = $csvData[$i]['sku']; + + $configSimpleproduct = $this->productRepository->create($data); + } else { + $configSimpleproduct = $productData; + } + + unset($data); + + $validateVariant = Validator::make($csvData[$i], [ + 'sku' => ['required', 'unique:products,sku,' . $configSimpleproduct->id, new \Webkul\Core\Contracts\Validations\Slug], + 'name' => 'required', + 'super_attribute_price' => 'required', + 'super_attribute_weight' => 'required', + 'super_attribute_option' => 'required', + 'super_attributes' => 'required' + ]); + + if ($validateVariant->fails()) { + $errors = $validateVariant->errors()->getMessages(); + + $this->helperRepository->deleteProductIfNotValidated($product->id); + + foreach($errors as $key => $error) { + $errorToBeReturn[] = str_replace(".", "", $error[0]). " for sku " .$csvData[$i]['sku']; + } + + $productUploadedWithError = $requestData['productUploaded'] + 1; + + $requestData['countOfStartedProfiles'] = $i + 1; + + if ($requestData['numberOfCSVRecord'] != 0) { + $remainDataInCSV = $requestData['totalNumberOfCSVRecord'] - $productUploadedWithError; + } else { + $remainDataInCSV = 0; + } + + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $requestData['productUploaded'], + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => $errorToBeReturn, + ); + + return $dataToBeReturn; + } + + $inventory_data = core()->getCurrentChannel()->inventory_sources; + + foreach($inventory_data as $key => $datas) { + $inventoryId = $datas->id; + } + + $inventoryData[] = (string)$csvData[$i]['super_attribute_qty']; + + foreach ($inventoryData as $key => $d) { + $inventory[$inventoryId] = $d; + } + + $productInventory = $this->productInventoryRepository->findOneByField('product_id', $configSimpleproduct->id); + + if (! isset($productInventory) && empty($productInventory) || $productInventory->count() < 1) { + $data['inventories'] = $inventory; + } + + $superAttributes = explode(',', $csvData[$i]['super_attributes']); + $superAttributesOption = explode(',', $csvData[$i]['super_attribute_option']); + + $data['super_attributes'] = array_combine($superAttributes, $superAttributesOption); + + if (isset($data['super_attributes']) && $i == $current) { + $super_attributes = []; + + foreach ($data['super_attributes'] as $attributeCode => $attributeOptions) { + $attribute = $this->attributeRepository->findOneByField('code', $attributeCode); + + $super_attributes[$attribute->id] = $attributeOptions; + + $users = $product->super_attributes()->where('id', $attribute->id)->exists(); + + if (! $users) { + $product->super_attributes()->attach($attribute->id); + } + } + } + + $data['dataFlowProfileRecordId'] = $dataFlowProfileRecord->id; + $data['channel'] = core()->getCurrentChannel()->code; + + $dataProfile = app('Webkul\Bulkupload\Repositories\DataFlowProfileRepository')->findOneByfield(['id' => $data['dataFlowProfileRecordId']]); + $data['locale'] = $dataProfile->locale_code; + + $data['price'] = (string)$csvData[$i]['super_attribute_price']; + $data['special_price'] = (string)$csvData[$i]['special_price']; + $data['special_price_from'] = (string)$csvData[$i]['special_price_from']; + $data['special_price_to'] = (string)$csvData[$i]['special_price_to']; + $data['new'] = (string)$csvData[$i]['new']; + $data['featured'] = (string)$csvData[$i]['featured']; + $data['visible_individually'] = (string)$csvData[$i]['visible_individually']; + $data['tax_category_id'] = (string)$csvData[$i]['tax_category_id']; + $data['cost'] = (string)$csvData[$i]['cost']; + $data['width'] = (string)$csvData[$i]['width']; + $data['height'] = (string)$csvData[$i]['height']; + $data['depth'] = (string)$csvData[$i]['depth']; + $data['status'] = (string)$csvData[$i]['status']; + $data['attribute_family_id'] = $attributeFamilyData->id; + $data['short_description'] = (string)$csvData[$i]['short_description']; + $data['sku'] = (string)$csvData[$i]['sku']; + $data['name'] = (string)$csvData[$i]['name']; + $data['weight'] = (string)$csvData[$i]['super_attribute_weight']; + $data['status'] = (string)$csvData[$i]['status']; + + if ( isset($data['super_attributes'])) { + foreach ($data['super_attributes'] as $attributeCode => $attributeOptions) { + $attribute = $this->attributeRepository->findOneByField('code', $attributeCode); + + if ( $attribute ) { + $attributeOptionColor = $this->attributeOptionRepository->findOneWhere([ + 'attribute_id' => $attribute->id, + 'admin_name' => $attributeOptions, + ]); + + $data[$attributeCode] = $attributeOptionColor->id; + } + } + } + + $individualProductimages = explode(',', $csvData[$i]['images']); + + if (isset($imageZipName)) { + $images = Storage::disk('local')->files('public/imported-products/extracted-images/admin/'.$dataFlowProfileRecord->id.'/'.$imageZipName['dirname'].'/'); + + foreach ($images as $imageArraykey => $imagePath) { + $imageName = explode('/', $imagePath); + + if (in_array(last($imageName), preg_replace('/[\'"]/', '',$individualProductimages))) { + $data['images'][$imageArraykey] = $imagePath; + } + } + } else if (isset($csvData['images'])) { + foreach ($individualProductimages as $imageArraykey => $imageURL) + { + if (filter_var(trim($imageURL), FILTER_VALIDATE_URL)) { + $imagePath = storage_path('app/public/ imported-products/extracted-images/admin/'. $dataFlowProfileRecord->id); + + if (!file_exists($imagePath)) { + mkdir($imagePath, 0777, true); + } + + $imageFile = $imagePath.'/'.basename($imageURL) ; + + file_put_contents($imageFile, file_get_contents (trim($imageURL))); + + $data['images'][$imageArraykey] = $imageFile; + } + } + } + + $configSimpleProductAttributeStore = $this->bulkProductRepository->productRepositoryUpdateForVariants($data, $configSimpleproduct->id); + + if (isset($imageZipName)) { + $this->productImageRepository->bulkuploadImages($data, $configSimpleproduct, $imageZipName); + } else if (isset($csvData['images'])) { + $this->productImageRepository->bulkuploadImages($data, $configSimpleproduct, $imageZipName = null); + } + + $configSimpleProductAttributeStore['parent_id'] = $product['productFlatId']; + + $this->createFlat($configSimpleProductAttributeStore); + + } else { + $savedProduct = $requestData['productUploaded'] + 1; + $remainDataInCSV = $requestData['totalNumberOfCSVRecord'] - $savedProduct; + $productsUploaded = $savedProduct; + + $requestData['countOfStartedProfiles'] = $product['loopCount']; + + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'] + ); + + return $dataToBeReturn; + } + } + + if ($requestData['errorCount'] == 0) { + $dataToBeReturn = [ + 'remainDataInCSV' => 0, + 'productsUploaded' => $requestData['totalNumberOfCSVRecord'], + 'countOfStartedProfiles' => count($csvData), + ]; + + return $dataToBeReturn; + } else { + $dataToBeReturn = [ + 'remainDataInCSV' => 0, + 'productsUploaded' => $requestData['totalNumberOfCSVRecord'] - $requestData['errorCount'], + 'countOfStartedProfiles' => count($csvData), + ]; + + return $dataToBeReturn; + } + + $product['productFlatId'] = null; + } catch (\Exception $e) { + $error = $e; + $requestData['countOfStartedProfiles'] = $i + 1; + $remainDataInCSV = $requestData['totalNumberOfCSVRecord'] - $requestData['productUploaded']; + + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $requestData['productUploaded'], + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => $error->errorInfo[2] ?? $error->getMessage(), + ); + + return $dataToBeReturn; + } + } + } + } + } + } + } catch(\Exception $e) { + \Log::error('configurable create product log: '. $e->getMessage()); + } + } + + /** + * create product flat for variants + * + * @param \Webkul\Product\Contracts\Product $product + * + * @return mixed + */ + public function createFlat($product, $parentProduct = null) + { + static $familyAttributes = []; + + static $superAttributes = []; + + if (! array_key_exists($product->attribute_family->id, $familyAttributes)) + $familyAttributes[$product->attribute_family->id] = $product->attribute_family->custom_attributes; + + if ($parentProduct && ! array_key_exists($parentProduct->id, $superAttributes)) + $superAttributes[$parentProduct->id] = $parentProduct->super_attributes()->pluck('code')->toArray(); + + foreach (core()->getAllChannels() as $channel) { + foreach ($channel->locales as $locale) { + $productFlat = $this->productFlatRepository->findOneWhere([ + 'product_id' => $product->id, + 'channel' => $channel->code, + 'locale' => $locale->code + ]); + + if (! $productFlat) { + $productFlat = $this->productFlatRepository->create([ + 'product_id' => $product->id, + 'channel' => $channel->code, + 'locale' => $locale->code + ]); + } + foreach ($familyAttributes[$product->attribute_family->id] as $attribute) { + if ($parentProduct && ! in_array($attribute->code, array_merge($superAttributes[$parentProduct->id], ['sku', 'name', 'price', 'weight', 'status']))) + continue; + + if (in_array($attribute->code, ['tax_category_id'])) + continue; + + if (! Schema::hasColumn('product_flat', $attribute->code)) + continue; + + if ($attribute->value_per_channel) { + if ($attribute->value_per_locale) { + $productAttributeValue = $product->attribute_values()->where('channel', $channel->code)->where('locale', $locale->code)->where('attribute_id', $attribute->id)->first(); + } else { + $productAttributeValue = $product->attribute_values()->where('channel', $channel->code)->where('attribute_id', $attribute->id)->first(); + } + } else { + if ($attribute->value_per_locale) { + $productAttributeValue = $product->attribute_values()->where('locale', $locale->code)->where('attribute_id', $attribute->id)->first(); + } else { + $productAttributeValue = $product->attribute_values()->where('attribute_id', $attribute->id)->first(); + } + } + + if ($product->type == 'configurable' && $attribute->code == 'price') { + try { + $productFlat->{$attribute->code} = app('Webkul\Bulkupload\Helpers\Price')->getVariantMinPrice($product); + } catch(\Exception $e) {} + } else { + try { + $productFlat->{$attribute->code} = $productAttributeValue[ProductAttributeValue::$attributeTypeFields[$attribute->type]]; + } catch(\Exception $e) {} + } + + if ($attribute->type == 'select') { + $attributeOption = $this->attributeOptionRepository->find($product->{$attribute->code}); + + if ($attributeOption) { + if ($attributeOptionTranslation = $attributeOption->translate($locale->code)) { + $productFlat->{$attribute->code . '_label'} = $attributeOptionTranslation->label; + } else { + $productFlat->{$attribute->code . '_label'} = $attributeOption->admin_name; + } + } + } elseif ($attribute->type == 'multiselect') { + $attributeOptionIds = explode(',', $product->{$attribute->code}); + + if (count($attributeOptionIds)) { + $attributeOptions = $this->attributeOptionRepository->findWhereIn('id', $attributeOptionIds); + + $optionLabels = []; + + foreach ($attributeOptions as $attributeOption) { + if ($attributeOptionTranslation = $attributeOption->translate($locale->code)) { + $optionLabels[] = $attributeOptionTranslation->label; + } else { + $optionLabels[] = $attributeOption->admin_name; + } + } + + $productFlat->{$attribute->code . '_label'} = implode(', ', $optionLabels); + } + } + } + + $productFlat->created_at = $product->created_at; + + $productFlat->updated_at = $product->updated_at; + + if ($parentProduct) { + $parentProductFlat = $this->productFlatRepository->findOneWhere([ + 'product_id' => $parentProduct->id, + 'channel' => $channel->code, + 'locale' => $locale->code + ]); + } + + $productFlat->parent_id = $product->parent_id; + + $productFlat->save(); + + $product->parent_id--; + } + } + } +} \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Repositories/Products/DownloadableProductRepository.php b/packages/Webkul/Bulkupload/src/Repositories/Products/DownloadableProductRepository.php new file mode 100644 index 000000000..13f2b29e1 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Repositories/Products/DownloadableProductRepository.php @@ -0,0 +1,789 @@ +importProductRepository = $importProductRepository; + + $this->productDownloadableLinkRepository = $productDownloadableLinkRepository; + + $this->categoryRepository = $categoryRepository; + + $this->productFlatRepository = $productFlatRepository; + + $this->productRepository = $productRepository; + + $this->productImageRepository = $productImageRepository; + + $this->attributeFamilyRepository = $attributeFamilyRepository; + + $this->helperRepository = $helperRepository; + + $this->attributeOptionRepository = $attributeOptionRepository; + } + + /* + * Specify Model class name + * + * @return mixed + */ + function model() + { + return 'Webkul\Product\Contracts\Product'; + } + + /** + * create & update downloadable-type product + * + * @param array $requestData + * @param array $imageZipName + * + * @return mixed + */ + public function createProduct($requestData, $imageZipName) + { + $uploadLinkFilesZipName = null; + $uploadSampleFilesZipName = null; + $uploadLinkSampleFilesZipName = null; + + try { + $dataFlowProfileRecord = $this->importProductRepository->findOneByField + ('data_flow_profile_id', $requestData['data_flow_profile_id']); + + $csvData = (new DataGridImport)->toArray($dataFlowProfileRecord->file_path)[0]; + + $downloadableLinks = $this->extractDownloadableFiles($dataFlowProfileRecord); + + if ($requestData['totalNumberOfCSVRecord'] < 1000) { + $processCSVRecords = $requestData['totalNumberOfCSVRecord']/($requestData['totalNumberOfCSVRecord']/10); + } else { + $processCSVRecords = $requestData['totalNumberOfCSVRecord']/($requestData['totalNumberOfCSVRecord']/100); + } + + $uptoProcessCSVRecords = (int)$requestData['countOfStartedProfiles'] + 10; + $processRecords = (int)$requestData['countOfStartedProfiles'] + (int)$requestData['numberOfCSVRecord']; + + if ($requestData['numberOfCSVRecord'] > $processCSVRecords) { + for ($i = $requestData['countOfStartedProfiles']; $i < $uptoProcessCSVRecords; $i++) { + $invalidateProducts = $this->store($csvData[$i], $i, $dataFlowProfileRecord, $requestData, $imageZipName, $downloadableLinks); + + if (isset($invalidateProducts) && !empty($invalidateProducts)) { + return $invalidateProducts; + } + } + } else if ($requestData['numberOfCSVRecord'] <= 10) { + for ($i = $requestData['countOfStartedProfiles']; $i < $processRecords; $i++) { + $invalidateProducts = $this->store($csvData[$i], $i, $dataFlowProfileRecord, $requestData, $imageZipName, $downloadableLinks); + + if (isset($invalidateProducts) && !empty($invalidateProducts)) { + return $invalidateProducts; + } + } + } + + if ($requestData['numberOfCSVRecord'] > 10) { + $remainDataInCSV = (int)$requestData['numberOfCSVRecord'] - (int)$processCSVRecords; + } else { + $remainDataInCSV = 0; + + if ($requestData['errorCount'] > 0) { + $uptoProcessCSVRecords = $requestData['totalNumberOfCSVRecord'] - $requestData['errorCount']; + } else { + $uptoProcessCSVRecords = $processRecords; + } + } + + $requestData['countOfStartedProfiles'] = $i; + + $dataToBeReturn = [ + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $uptoProcessCSVRecords, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + ]; + + return $dataToBeReturn; + + } catch(\Exception $e) { + Log::error('downloadable create product log: '. $e->getMessage()); + + $categoryError = explode('[' ,$e->getMessage()); + $categorySlugError = explode(']' ,$e->getMessage()); + $requestData['countOfStartedProfiles'] = $i + 1; + $productsUploaded = $i - $requestData['errorCount']; + + if ($requestData['numberOfCSVRecord'] != 0) { + $remainDataInCSV = (int)$requestData['totalNumberOfCSVRecord'] - (int)$requestData['countOfStartedProfiles']; + } else { + $remainDataInCSV = 0; + } + + if ($categoryError[0] == "No query results for model ") { + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => "Invalid Category Slug: " . $categorySlugError[1], + ); + $categoryError[0] = null; + } else if (isset($e->errorInfo)) { + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => $e->errorInfo[2], + ); + } else { + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => $e->getMessage(), + ); + } + + return $dataToBeReturn; + } + } + + /** + * function to store product + * + * @param array $csvData + * @param integer $i + * @param \Webkul\Bulkupload\Contracts\ImportProduct $dataFlowProfileRecord + * @param array $requestData + * @param array $imageZipName + * @param array $downloadableLinks + * + * @return mixed + */ + public function store($csvData, $i, $dataFlowProfileRecord, $requestData, $imageZipName, $downloadableLinks) + { + $createValidation = $this->helperRepository->createProductValidation($csvData, $i); + + if (isset($createValidation)) { + return $createValidation; + } + + $d_samples = []; + $sampleNameKey = []; + $linkNameKey = []; + $d_links = []; + + if (isset($csvData['samples_title'])) { + $csvData['sample_sort_order'] = ""; + $sampleTitles = explode(',', $csvData['samples_title']) ; + $sampleType = explode(',', $csvData['sample_type']) ; + $sampleFiles = explode(',', $csvData['sample_files']) ; + $urlFiles = explode(',', $csvData['sample_url']) ; + $sampleSortOrder = !empty($csvData['sample_sort_order']) ? explode(',', $csvData['sample_sort_order']) : 0; + } + + //for downloadable link explode + if (isset($csvData['link_titles'])) { + $linkTitles = explode(',', $csvData['link_titles']); + $linkTypes = explode(',', $csvData['link_types']); + + $linkFileNames = explode(',', $csvData['link_file_names']); + + $linkPrices = !empty($csvData['link_prices']) ? explode(',', $csvData['link_prices']) : ""; + + $linkSampleTypes = !empty($csvData['link_sample_types']) ? explode(',', $csvData['link_sample_types']) : "file"; + + $linkSampleFileNames = !empty($csvData['link_sample_file_names']) ? explode(',', $csvData['link_sample_file_names']) : ""; + + $linkDownloads = !empty($csvData['link_downloads']) ? explode(',', $csvData['link_downloads']) : 0; + + $linkSortOrders = !empty($csvData['link_sort_orders']) ? explode(',', $csvData['link_sort_orders']) : 0; + + $linkSampleUrlNames = explode(',', $csvData['link_sample_url']); + $linkUrlNames = explode(',', $csvData['link_url']); + } + + $productFlatData = $this->productFlatRepository->findWhere(['sku' => $csvData['sku'], 'url_key' => $csvData['url_key']])->first(); + + $productData = $this->productRepository->findWhere(['sku' => $csvData['sku']])->first(); + + $attributeFamilyData = $this->attributeFamilyRepository->findOneByfield(['name' => $csvData['attribute_family_name']]); + + if (! isset($productFlatData) && empty($productFlatData)) { + $data['type'] = $csvData['type']; + $data['attribute_family_id'] = $attributeFamilyData->id; + $data['sku'] = $csvData['sku']; + + $downloadableProduct = $this->productRepository->create($data); + } else { + $downloadableProduct = $productData; + } + + unset($data); + $data = []; + $attributeCode = []; + $attributeValue = []; + + //default attributes + foreach ($downloadableProduct->getTypeInstance()->getEditableAttributes()->toArray() as $key => $value) { + $searchIndex = $value['code']; + if (array_key_exists($searchIndex, $csvData)) { + if (is_null($csvData[$searchIndex])) { + continue; + } + + array_push($attributeCode, $searchIndex); + + if ($searchIndex == "color" || $searchIndex == "size" || $searchIndex == "brand") { + $attributeOption = $this->attributeOptionRepository->findOneByField(['admin_name' => ucwords($csvData[$searchIndex])]); + + array_push($attributeValue, $attributeOption['id']); + } else { + array_push($attributeValue, $csvData[$searchIndex]); + } + + $data = array_combine($attributeCode, $attributeValue); + } + } + + $data['dataFlowProfileRecordId'] = $dataFlowProfileRecord->id; + + $categoryData = explode(',', $csvData['categories_slug']); + + if (is_null($csvData['categories_slug']) || empty($csvData['categories_slug'])) { + $categoryID = $this->categoryRepository->findBySlugOrFail('root')->id; + } else { + foreach ($categoryData as $key => $value) { + $categoryID[$key] = $this->categoryRepository->findBySlugOrFail($categoryData[$key])->id; + } + } + + $data['categories'] = $categoryID; + $data['channel'] = core()->getCurrentChannel()->code; + + $dataProfile = app('Webkul\Bulkupload\Repositories\DataFlowProfileRepository')->findOneByfield(['id' => $data['dataFlowProfileRecordId']]); + $data['locale'] = $dataProfile->locale_code; + + //customerGroupPricing + if (isset($csvData['customer_group_prices']) && ! empty($csvData['customer_group_prices'])) { + $data['customer_group_prices'] = json_decode($csvData['customer_group_prices'], true); + app(ProductCustomerGroupPriceRepository::class)->saveCustomerGroupPrices($data, $simpleproductData); + } + + + //prepare downloadable sample data + for ($j = 0; $j < count($sampleTitles); $j++) { + if (trim(strtolower($sampleType[$j])) == "file") { + if (isset($downloadableLinks['uploadSampleFilesZipName'])) { + if (trim(strtolower($sampleType[$j-1])) == "url") { + $sampleFileName = $sampleFiles[$j-1]; + } else { + $sampleFileName = $sampleFiles[$j]; + } + + $files = $this->fileOrUrlUpload($dataFlowProfileRecord, $sampleType[$j], $sampleFileName, $downloadableProduct->id, $downloadableLinks, $sampleFile = true); + + if (isset($files)) { + $sample['sample_'.$j] = [ + core()->getCurrentLocale()->code => [ + "title" => $sampleTitles[$j], + ], + "type" => trim($sampleType[$j]), + "file" => trim($files), + "file_name" => $sampleFileName, + "sort_order" => $sampleSortOrder[$j] ?? 0, + ]; + + array_push($sampleNameKey, 'sample_'.$j); + array_push($d_samples, $sample['sample_'.$j]); + } + } + } else if (trim(strtolower($sampleType[$j])) == "url") { + $files = $this->fileOrUrlUpload($dataFlowProfileRecord, $sampleType[$j], $urlFiles[$j], $downloadableProduct->id, $downloadableLinks, $sampleFile = true); + + if (isset($files)) { + $sample['sample_'.$j] = [ + core()->getCurrentLocale()->code => [ + "title" => $sampleTitles[$j], + ], + "type" => trim($sampleType[$j]), + "url" => trim($urlFiles[$j]), + "sort_order" => $sampleSortOrder[$j] ?? 0, + ]; + + array_push($sampleNameKey, 'sample_'.$j); + array_push($d_samples, $sample['sample_'.$j]); + } + } + } + + $combinedArray = array_combine($sampleNameKey, $d_samples); + + $data['downloadable_samples'] = $combinedArray; + + //for downloadable links + for ($j = 0; $j < count($linkTitles); $j++) { + if (trim(strtolower($linkTypes[$j])) == "file") { + if (trim(strtolower($linkSampleTypes[$j])) == "file") { + if (isset($downloadableLinks['uploadLinkSampleFilesZipName'])) { + if (trim(strtolower($linkSampleTypes[$j-1])) == "url") { + $linkSampleFile = $linkSampleFileNames[$j-1]; + } else { + $linkSampleFile = $linkSampleFileNames[$j]; + } + + $sampleFileLink = $this->fileOrUrlUpload($dataFlowProfileRecord, $linkSampleTypes[$j], $linkSampleFile, $downloadableProduct->id, $downloadableLinks, $sampleLinkfile = false); + } + } else if (trim(strtolower($linkSampleTypes[$j])) == "url") { + $sampleFileLink = $this->fileOrUrlUpload($dataFlowProfileRecord, $linkSampleTypes[$j], $linkSampleUrlNames[$j], $downloadableProduct->id, $downloadableLinks, $sampleLinkfile = false); + } + + if (isset($downloadableLinks['uploadLinkFilesZipName'])) { + if (trim(strtolower($linkSampleTypes[$j-1])) == "url") { + $linkFileName = $linkFileNames[$j-1]; + } else { + $linkFileName = $linkFileNames[$j]; + } + + $fileLink = $this->linkFileOrUrlUpload($dataFlowProfileRecord, $linkTypes[$j], $linkFileName, $downloadableProduct->id, $downloadableLinks); + } + + if (isset($fileLink)) { + $link['link_'.$j] = [ + core()->getCurrentLocale()->code => [ + "title" => $linkTitles[$j], + ], + "price" => $linkPrices[$j], + "type" => trim($linkTypes[$j]), + "file" => trim($fileLink), + "file_name" => $linkFileName, + "sample_type" => trim($linkSampleTypes[$j]), + "downloads" => $linkDownloads[$j] ?? 0, + "sort_order" => $linkSortOrders[$j] ?? 0, + ]; + + if (trim($linkSampleTypes[$j]) == "url") { + $link['link_'.$j]['sample_url'] = trim($linkSampleUrlNames[$j]); + } else if (trim($linkSampleTypes[$j]) == "file") { + $link['link_'.$j]['sample_file'] = trim($sampleFileLink); + + $link['link_'.$j]['sample_file_name'] = trim($linkSampleFile); + } + + array_push($linkNameKey, 'link_'.$j); + array_push($d_links, $link['link_'.$j]); + } + } else if (trim(strtolower($linkTypes[$j])) == "url") { + if (trim(strtolower($linkSampleTypes[$j])) == "file") { + if (isset($downloadableLinks['uploadLinkSampleFilesZipName'])) { + $sampleFileLink = $this->fileOrUrlUpload($dataFlowProfileRecord, $linkSampleTypes[$j], $linkSampleFileNames[$j], $downloadableProduct->id, $downloadableLinks, $sampleLinkfile = false); + } + } else if (trim(strtolower($linkSampleTypes[$j])) == "url") { + $sampleFileLink = $this->fileOrUrlUpload($dataFlowProfileRecord, $linkSampleTypes[$j], $linkSampleUrlNames[$j], $downloadableProduct->id, $downloadableLinks, $sampleLinkfile = false); + } + + $fileLink = $this->linkFileOrUrlUpload($dataFlowProfileRecord, $linkTypes[$j], $linkUrlNames[$j], $downloadableProduct->id, $downloadableLinks); + + if (isset($fileLink)) { + $link['link_'.$j] = [ + core()->getCurrentLocale()->code => [ + "title" => $linkTitles[$j], + ], + "price" => $linkPrices[$j], + "type" => trim($linkTypes[$j]), + "url" => trim($linkUrlNames[$j]) ?? "", + "sample_type" => trim($linkSampleTypes[$j]), + "downloads" => $linkDownloads[$j] ?? 0, + "sort_order" => $linkSortOrders[$j] ?? 0, + ]; + + if (trim($linkSampleTypes[$j]) == "url") { + $link['link_'.$j]['sample_url'] = trim($linkSampleUrlNames[$j]); + } else if (trim($linkSampleTypes[$j]) == "file") { + $link['link_'.$j]['sample_file'] = trim($sampleFileLink); + $link['link_'.$j]['sample_file_name'] = trim($linkSampleFileNames[$j]); + } + + array_push($linkNameKey, 'link_'.$j); + array_push($d_links, $link['link_'.$j]); + } + } + } + + $combinedLinksArray = array_combine($linkNameKey, $d_links); + + $data['downloadable_links'] = $combinedLinksArray; + + //Product Images + $individualProductimages = explode(',', $csvData['images']); + + if (isset($imageZipName)) { + $images = Storage::disk('local')->files('public/imported-products/extracted-images/admin/'.$dataFlowProfileRecord->id.'/'.$imageZipName['dirname'].'/'); + + foreach ($images as $imageArraykey => $imagePath) { + $imageName = explode('/', $imagePath); + + if (in_array(last($imageName), preg_replace('/[\'"]/', '',$individualProductimages))) { + $data['images'][$imageArraykey] = $imagePath; + } + } + } else if (isset($csvData['images'])) { + foreach ($individualProductimages as $imageArraykey => $imageURL) + { + if (filter_var(trim($imageURL), FILTER_VALIDATE_URL)) { + $imagePath = storage_path('app/public/imported-products/extracted-images/ admin/'.$dataFlowProfileRecord->id); + + if (!file_exists($imagePath)) { + mkdir($imagePath, 0777, true); + } + + $imageFile = $imagePath.'/'.basename($imageURL); + + file_put_contents($imageFile, file_get_contents(trim($imageURL))); + + $data['images'][$imageArraykey] = $imageFile; + } + } + } + + $validationRules = $this->helperRepository->validateCSV($requestData['data_flow_profile_id'], $data, $dataFlowProfileRecord, $downloadableProduct); + + $csvValidator = Validator::make($data, $validationRules); + + if ($csvValidator->fails()) { + $errors = $csvValidator->errors()->getMessages(); + + $this->helperRepository->deleteProductIfNotValidated($downloadableProduct->id); + + foreach($errors as $key => $error){ + if ($error[0] == "The url key has already been taken.") { + $errorToBeReturn[] = "The url key " . $data['url_key'] . " has already been taken"; + } else { + $errorToBeReturn[] = str_replace(".", "", $error[0]). " for sku " . $data['sku']; + } + } + + $requestData['countOfStartedProfiles'] = $i + 1; + + $productsUploaded = $i - $requestData['errorCount']; + + if ($requestData['numberOfCSVRecord'] != 0) { + $remainDataInCSV = (int)$requestData['totalNumberOfCSVRecord'] - (int)$requestData['countOfStartedProfiles']; + } else { + $remainDataInCSV = 0; + } + + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => $errorToBeReturn, + ); + + return $dataToBeReturn; + } + + $this->productRepository->update($data, $downloadableProduct->id); + + if (isset($imageZipName)) { + $this->productImageRepository->bulkuploadImages($data, $downloadableProduct, $imageZipName); + } else if (isset($csvData['images'])) { + $this->productImageRepository->bulkuploadImages($data, $downloadableProduct, $imageZipName = null); + } + } + + /** + * upload sample file link or url + * + * @param \Webkul\Bulkupload\Contracts\ImportProduct $dataFlowProfileRecord + * @param string $type + * @param string|array $file + * @param integer $id + * @param array $downloadableLinks + * @param string $flag + * + * @return mixed + */ + public function fileOrUrlUpload($dataFlowProfileRecord, $type, $file, $id, $downloadableLinks, $flag) + { + try { + if (trim($type) == "file") { + if ($flag) { + $files = "imported-products/extracted-images/admin/sample-files/".$dataFlowProfileRecord->id.'/'. $downloadableLinks['uploadSampleFilesZipName']['dirname'].'/'.trim(basename($file)); + + $destination = "product/".$id.'/'.trim(basename($file)); + + Storage::copy($files, $destination); + + return $destination; + } else { + $files = "imported-products/extracted-images/admin/link-sample-files/".$dataFlowProfileRecord->id.'/'. $downloadableLinks['uploadLinkSampleFilesZipName']['dirname'].'/'.trim(basename($file)); + + $destination = "product/".$id.'/'.trim(basename($file)); + + Storage::copy($files, $destination); + + return $destination; + } + } else { + if ($flag) { + $imagePath = storage_path('app/public/imported-products/extracted-images/admin/sample-files/'.$dataFlowProfileRecord->id); + + if (!file_exists($imagePath)) { + mkdir($imagePath, 0777, true); + } + + $imageFile = $imagePath.'/'.basename($file); + + file_put_contents($imageFile, file_get_contents(trim($file))); + + $files = "imported-products/extracted-images/admin/sample-files/".$dataFlowProfileRecord->id.'/'.basename($file); + + $destination = "product/".$id.'/'.basename($file); + Storage::copy($files, $destination); + + return $destination; + } else { + $imagePath = storage_path('app/public/imported-products/extracted-images/admin/link-sample-files/'.$dataFlowProfileRecord->id); + + if (!file_exists($imagePath)) { + mkdir($imagePath, 0777, true); + } + + $imageFile = $imagePath.'/'.basename($file); + + file_put_contents($imageFile, file_get_contents(trim($file))); + + $files = "imported-products/extracted-images/admin/link-sample-files/".$dataFlowProfileRecord->id.'/'.basename($file); + + $destination = "product/".$id.'/'.basename($file); + Storage::copy($files, $destination); + + return $destination; + } + } + } catch(\Exception $e) { + Log::error('downloadable fileOrUrlUpload log: '. $e->getMessage()); + } + } + + /** + * upload link file or url + * + * @param \Webkul\Bulkupload\Contracts\ImportProduct $dataFlowProfileRecord + * @param string $type + * @param string|array $file + * @param integer $id + * @param array $downloadableLinks + * + * @return mixed + */ + public function linkFileOrUrlUpload($dataFlowProfileRecord, $type, $file, $id, $downloadableLinks) + { + try { + if (trim($type) == "file") { + $files = "imported-products/extracted-images/admin/link-files/".$dataFlowProfileRecord->id.'/'. $downloadableLinks['uploadLinkFilesZipName']['dirname'].'/'.trim(basename($file)); + + $destination = "product_downloadable_links/".$id.'/'.basename($file); + + Storage::copy($files, $destination); + + return $destination; + } else { + $imagePath = storage_path('app/public/imported-products/extracted-images/admin/link-files/'.$dataFlowProfileRecord->id); + + if (!file_exists($imagePath)) { + mkdir($imagePath, 0777, true); + } + + $imageFile = $imagePath.'/'.basename($file); + + file_put_contents($imageFile, file_get_contents(trim($file))); + + $files = "imported-products/extracted-images/admin/link-files/".$dataFlowProfileRecord->id.'/'.basename($file); + + $destination = "product_downloadable_links/".$id.'/'.basename($file); + + Storage::copy($files, $destination); + + return $destination; + } + } catch(\Exception $e) { + Log::error('downloadable linkFileOrUrlUpload log: '. $e->getMessage()); + } + } + + /** + * unzip zip files and store in storage folder + * + * @param \Webkul\Bulkupload\Contracts\ImportProduct $record + * + * @return mixed + */ + public function extractDownloadableFiles($record) + { + if (isset($record->upload_link_files) && ($record->upload_link_files != "") ) { + $uploadLinkFilesZip = new \ZipArchive(); + + $extractedPath = storage_path('app/public/imported-products/extracted-images/admin/link-files/'.$record->id.'/'); + + if ($uploadLinkFilesZip->open(storage_path('app/public/'.$record->upload_link_files))) { + for ($i = 0; $i < $uploadLinkFilesZip->numFiles; $i++) { + $filename = $uploadLinkFilesZip->getNameIndex($i); + $uploadLinkFilesZipName = pathinfo($filename); + } + + $uploadLinkFilesZip->extractTo($extractedPath); + $uploadLinkFilesZip->close(); + } + } else { + $uploadLinkFilesZipName = null; + } + + if (isset($record->upload_sample_files) && ($record->upload_sample_files != "") ) { + $uploadSampleFilesZip = new \ZipArchive(); + + $extractedPath = storage_path('app/public/imported-products/extracted-images/admin/sample-files/'.$record->id.'/'); + + if ($uploadSampleFilesZip->open(storage_path('app/public/'.$record->upload_sample_files))) { + for ($i = 0; $i < $uploadSampleFilesZip->numFiles; $i++) { + $filename = $uploadSampleFilesZip->getNameIndex($i); + $uploadSampleFilesZipName = pathinfo($filename); + } + + $uploadSampleFilesZip->extractTo($extractedPath); + $uploadSampleFilesZip->close(); + } + } else { + $uploadSampleFilesZipName = null; + } + + if (isset($record->upload_link_sample_files) && ($record->upload_link_sample_files != "") ) { + $uploadLinkSampleFilesZip = new \ZipArchive(); + + $extractedPath = storage_path('app/public/imported-products/extracted-images/admin/link-sample-files/'.$record->id.'/'); + + if ($uploadLinkSampleFilesZip->open(storage_path('app/public/'.$record->upload_link_sample_files))) { + for ($i = 0; $i < $uploadLinkSampleFilesZip->numFiles; $i++) { + $filename = $uploadLinkSampleFilesZip->getNameIndex($i); + $uploadLinkSampleFilesZipName = pathinfo($filename); + } + + $uploadLinkSampleFilesZip->extractTo($extractedPath); + $uploadLinkSampleFilesZip->close(); + } + } else { + $uploadLinkSampleFilesZipName = null; + } + + return [ + 'uploadLinkSampleFilesZipName' => $uploadLinkSampleFilesZipName, 'uploadSampleFilesZipName' => $uploadSampleFilesZipName, + 'uploadLinkFilesZipName' => $uploadLinkFilesZipName + ]; + } +} \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Repositories/Products/GroupedProductRepository.php b/packages/Webkul/Bulkupload/src/Repositories/Products/GroupedProductRepository.php new file mode 100644 index 000000000..c12b99af2 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Repositories/Products/GroupedProductRepository.php @@ -0,0 +1,440 @@ +importProductRepository = $importProductRepository; + + $this->categoryRepository = $categoryRepository; + + $this->productFlatRepository = $productFlatRepository; + + $this->attributeOptionRepository = $attributeOptionRepository; + + $this->productRepository = $productRepository; + + $this->productImageRepository = $productImageRepository; + + $this->attributeFamilyRepository = $attributeFamilyRepository; + + $this->helperRepository = $helperRepository; + } + + /* + * Specify Model class name + * + * @return mixed + */ + function model() + { + return 'Webkul\Product\Contracts\Product'; + } + + /** + * create & update grouped-type product + * + * @param array $requestData + * @param array $imageZipName + * @param array $product + * + * @return mixed + */ + public function createProduct($requestData, $imageZipName) + { + try { + $dataFlowProfileRecord = $this->importProductRepository->findOneByField + ('data_flow_profile_id', $requestData['data_flow_profile_id']); + + $csvData = (new DataGridImport)->toArray($dataFlowProfileRecord->file_path)[0]; + + if ($requestData['totalNumberOfCSVRecord'] < 1000) { + $processCSVRecords = $requestData['totalNumberOfCSVRecord']/($requestData['totalNumberOfCSVRecord']/10); + } else { + $processCSVRecords = $requestData['totalNumberOfCSVRecord']/($requestData['totalNumberOfCSVRecord']/100); + } + + $uptoProcessCSVRecords = (int)$requestData['countOfStartedProfiles'] + 10; + $processRecords = (int)$requestData['countOfStartedProfiles'] + (int)$requestData['numberOfCSVRecord']; + + $inventory = []; + + if ($requestData['numberOfCSVRecord'] > $processCSVRecords) { + for ($i = $requestData['countOfStartedProfiles']; $i < $uptoProcessCSVRecords; $i++) { + $invalidateProducts = $this->store($csvData[$i], $i, $dataFlowProfileRecord, $requestData, $imageZipName); + + if (isset($invalidateProducts) && !empty($invalidateProducts)) { + return $invalidateProducts; + } + } + } else if ($requestData['numberOfCSVRecord'] <= 10) { + for ($i = $requestData['countOfStartedProfiles']; $i < $processRecords; $i++) { + $invalidateProducts = $this->store($csvData[$i], $i, $dataFlowProfileRecord, $requestData, $imageZipName); + + if (isset($invalidateProducts) && !empty($invalidateProducts)) { + return $invalidateProducts; + } + } + } + + if ($requestData['numberOfCSVRecord'] > 10) { + $remainDataInCSV = (int)$requestData['numberOfCSVRecord'] - (int)$processCSVRecords; + } else { + $remainDataInCSV = 0; + + if($requestData['errorCount'] > 0) { + $uptoProcessCSVRecords = $requestData['totalNumberOfCSVRecord'] - $requestData['errorCount']; + } else { + $uptoProcessCSVRecords = $processRecords; + } + } + + $requestData['countOfStartedProfiles'] = $i; + + $dataToBeReturn = [ + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $uptoProcessCSVRecords, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + ]; + + return $dataToBeReturn; + } catch(\Exception $e) { + Log::error('grouped create product log: '. $e->getMessage()); + + $categoryError = explode('[' ,$e->getMessage()); + $categorySlugError = explode(']' ,$e->getMessage()); + $requestData['countOfStartedProfiles'] = $i + 1; + $productsUploaded = $i - $requestData['errorCount']; + + if ($requestData['numberOfCSVRecord'] != 0) { + $remainDataInCSV = (int)$requestData['totalNumberOfCSVRecord'] - (int)$requestData['countOfStartedProfiles']; + } else { + $remainDataInCSV = 0; + } + + if ($categoryError[0] == "No query results for model ") { + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => "Invalid Category Slug: " . $categorySlugError[1], + ); + $categoryError[0] = null; + } else if (isset($e->errorInfo)) { + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => $e->errorInfo[2], + ); + } else { + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => $e->getMessage(), + ); + } + + return $dataToBeReturn; + } + } + + + /** + * function to store product + * + * @param array $csvData + * @param integer $i + * @param \Webkul\Bulkupload\Contracts\ImportProduct $dataFlowProfileRecord + * @param array $requestData + * @param array $imageZipName + * + * @return mixed + */ + public function store($csvData, $i, $dataFlowProfileRecord, $requestData, $imageZipName) + { + $createValidation = $this->helperRepository->createProductValidation($csvData, $i); + + if (isset($createValidation)) { + return $createValidation; + } + + $productFlatData = $this->productFlatRepository->findWhere(['sku' => $csvData['sku'], 'url_key' => $csvData['url_key']])->first(); + + $productData = $this->productRepository->findWhere(['sku' => $csvData['sku']])->first(); + + $attributeFamilyData = $this->attributeFamilyRepository->findOneByfield(['name' => $csvData['attribute_family_name']]); + + if (! isset($productFlatData) && empty($productFlatData)) { + $data['type'] = $csvData['type']; + $data['attribute_family_id'] = $attributeFamilyData->id; + $data['sku'] = $csvData['sku']; + + $groupedProduct = $this->productRepository->create($data); + } else { + $groupedProduct = $productData; + } + + unset($data); + $data = []; + $attributeCode = []; + $attributeValue = []; + + //default attributes + foreach ($groupedProduct->getTypeInstance()->getEditableAttributes()->toArray() as $key => $value) { + $searchIndex = $value['code']; + + if (array_key_exists($searchIndex, $csvData)) { + if (is_null($csvData[$searchIndex])) { + continue; + } + + array_push($attributeCode, $searchIndex); + + if ($searchIndex == "color" || $searchIndex == "size" || $searchIndex == "brand") { + $attributeOption = $this->attributeOptionRepository->findOneByField(['admin_name' => ucwords($csvData[$searchIndex])]); + + array_push($attributeValue, $attributeOption['id']); + } else { + array_push($attributeValue, $csvData[$searchIndex]); + } + + $data = array_combine($attributeCode, $attributeValue); + } + } + + $data['dataFlowProfileRecordId'] = $dataFlowProfileRecord->id; + + $categoryData = explode(',', $csvData['categories_slug']); + + if (is_null($csvData['categories_slug']) || empty($csvData['categories_slug'])) { + $categoryID = $this->categoryRepository->findBySlugOrFail('root')->id; + } else { + foreach ($categoryData as $key => $value) { + $categoryID[$key] = $this->categoryRepository->findBySlugOrFail($categoryData[$key])->id; + } + } + + $data['categories'] = $categoryID; + $data['channel'] = core()->getCurrentChannel()->code; + + $dataProfile = app('Webkul\Bulkupload\Repositories\DataFlowProfileRepository')->findOneByfield(['id' => $data['dataFlowProfileRecordId']]); + $data['locale'] = $dataProfile->locale_code; + + //customerGroupPricing + if (isset($csvData['customer_group_prices']) && ! empty($csvData['customer_group_prices'])) { + $data['customer_group_prices'] = json_decode($csvData['customer_group_prices'], true); + app(ProductCustomerGroupPriceRepository::class)->saveCustomerGroupPrices($data, $simpleproductData); + } + + //grouped product links + if (isset($csvData['grouped_product_sku'])) { + $groupedProductSku = explode(",", strtolower($csvData['grouped_product_sku'])); + $groupedQuantity = explode(",", $csvData['grouped_quantity']); + $groupedSortOrder = explode(",", $csvData['grouped_sort_order']); + + for ($j = 0; $j < count($groupedProductSku); $j++) { + $link = $j+1; + + $variants = true; + + $associatedProducts = $this->productRepository->findOneByField(['sku' => strtolower(trim($groupedProductSku[$j]))]); + + if (isset($associatedProducts) && !empty($associatedProducts)) { + if (isset($associatedProducts->parent_id)) { + $groupedLink['link_'.$link] = [ + "associated_product_id" => $associatedProducts->id, + "qty" => $groupedQuantity[$j], + "sort_order" => $groupedSortOrder[$j], + ]; + } else if ($associatedProducts->type == "simple") { + $groupedLink['link_'.$link] = [ + "associated_product_id" => $associatedProducts->id, + "qty" => $groupedQuantity[$j], + "sort_order" => $groupedSortOrder[$j], + ]; + } + } + } + + if(isset($groupedLink) && !empty($groupedLink)) { + $data['links'] = $groupedLink; + } + } + + //Product Images + $individualProductimages = explode(',', $csvData['images']); + + if (isset($imageZipName)) { + $images = Storage::disk('local')->files('public/imported-products/extracted-images/admin/'.$dataFlowProfileRecord->id.'/'.$imageZipName['dirname'].'/'); + + foreach ($images as $imageArraykey => $imagePath) { + $imageName = explode('/', $imagePath); + + if (in_array(last($imageName), preg_replace('/[\'"]/', '',$individualProductimages))) { + $data['images'][$imageArraykey] = $imagePath; + } + } + } else if (isset($csvData['images'])) { + foreach ($individualProductimages as $imageArraykey => $imageURL) + { + if (filter_var(trim($imageURL), FILTER_VALIDATE_URL)) { + $imagePath = storage_path('app/public/imported-products/extracted-images/admin/'.$dataFlowProfileRecord->id); + + if (!file_exists($imagePath)) { + mkdir($imagePath, 0777, true); + } + + $imageFile = $imagePath.'/'.basename($imageURL); + + file_put_contents($imageFile, file_get_contents(trim($imageURL))); + + $data['images'][$imageArraykey] = $imageFile; + } + } + } + + $validationRules = $this->helperRepository->validateCSV($requestData['data_flow_profile_id'], $data, $dataFlowProfileRecord, $groupedProduct); + + $csvValidator = Validator::make($data, $validationRules); + + if ($csvValidator->fails()) { + $errors = $csvValidator->errors()->getMessages(); + + $this->helperRepository->deleteProductIfNotValidated($groupedProduct->id); + + foreach ($errors as $key => $error) { + if ($error[0] == "The url key has already been taken.") { + $errorToBeReturn[] = "The url key " . $data['url_key'] . " has already been taken"; + } else { + $errorToBeReturn[] = str_replace(".", "", $error[0]). " for sku " . $data['sku']; + } + } + + $requestData['countOfStartedProfiles'] = $i + 1; + + $productsUploaded = $i - $requestData['errorCount']; + + if ($requestData['numberOfCSVRecord'] != 0) { + $remainDataInCSV = (int)$requestData['totalNumberOfCSVRecord'] - (int)$requestData['countOfStartedProfiles']; + } else { + $remainDataInCSV = 0; + } + + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => $errorToBeReturn, + ); + + return $dataToBeReturn; + } + + $this->productRepository->update($data, $groupedProduct->id); + + if (isset($imageZipName)) { + $this->productImageRepository->bulkuploadImages($data, $groupedProduct, $imageZipName); + } else if (isset($csvData['images'])) { + $this->productImageRepository->bulkuploadImages($data, $groupedProduct, $imageZipName = null); + } + } +} \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Repositories/Products/HelperRepository.php b/packages/Webkul/Bulkupload/src/Repositories/Products/HelperRepository.php new file mode 100644 index 000000000..72cab7399 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Repositories/Products/HelperRepository.php @@ -0,0 +1,217 @@ +dataFlowProfileRepository = $dataFlowProfileRepository; + + $this->productAttributeValueRepository = $productAttributeValueRepository; + + $this->productFlatRepository = $productFlatRepository; + + $this->productRepository = $productRepository; + } + + /** + * Specify Model class name + * + * @return string + */ + function model() + { + return 'Webkul\Product\Contracts\Product'; + } + + /** + * validation Rules for creating product + * + * @param integer $dataFlowProfileId + * @param array $records + * @param \Webkul\Bulkupload\Contracts\ImportProduct $dataFlowProfileRecord + * @param \Webkul\Product\Contracts\Product $product + * @return array + */ + public function validateCSV($dataFlowProfileId, $records, $dataFlowProfileRecord, $product) + { + $messages = []; + $rules = []; + + $profiler = $this->dataFlowProfileRepository->findOneByField('id', $dataFlowProfileId); + + if ($dataFlowProfileRecord) { + foreach($records as $data) { + $this->rules = array_merge($product->getTypeInstance()->getTypeValidationRules(), [ + 'sku' => ['required', 'unique:products,sku,' . $product->id, new \Webkul\Core\Contracts\Validations\Slug], + 'special_price_from' => 'nullable|date', + 'special_price_to' => 'nullable|date|after_or_equal:special_price_from', + 'special_price' => ['nullable', new \Webkul\Core\Contracts\Validations\Decimal, 'lt:price'], + ]); + + foreach ($product->getEditableAttributes() as $attribute) { + if ($attribute->code == 'sku' || $attribute->type == 'boolean') { + continue; + } + + $validations = []; + + if (! isset($this->rules[$attribute->code])) { + array_push($validations, $attribute->is_required ? 'required' : 'nullable'); + } else { + $validations = $this->rules[$attribute->code]; + } + + if ($attribute->type == 'text' && $attribute->validation) { + array_push($validations, + $attribute->validation == 'decimal' + ? new \Webkul\Core\Contracts\Validations\Decimal + : $attribute->validation + ); + } + + if ($attribute->type == 'price') { + array_push($validations, new \Webkul\Core\Contracts\Validations\Decimal); + } + + if ($attribute->is_unique) { + $this->id = $product; + + array_push($validations, function ($field, $value, $fail) use ($attribute) { + $column = ProductAttributeValue::$attributeTypeFields[$attribute->type]; + + if (! $this->productAttributeValueRepository->isValueUnique($this->id, $attribute->id, $column, request($attribute->code))) { + $fail('The :attribute has already been taken.'); + } + }); + } + + $this->rules[$attribute->code] = $validations; + } + + $validationCheckForUpdateData = $this->productFlatRepository + ->findWhere(['sku' => $records['sku'], 'url_key' => $records['url_key']]); + + if (is_null($validationCheckForUpdateData) || empty($validationCheckForUpdateData)) { + $urlKeyUniqueness = "unique:product_flat,url_key"; + array_push($this->rules["url_key"], $urlKeyUniqueness); + } + + return $this->rules; + } + } + } + + /** + * delete Product if validation fails + * + * @param integer $id + * @return void + */ + public function deleteProductIfNotValidated($id) + { + $this->productRepository->findOrFail($id)->delete(); + } + + /** + * Validation check for product creation + * + * @param array $record + * @param integer $loopCount + * @return void + */ + public function createProductValidation($record, $loopCount) + { + try { + $validateProduct = Validator::make($record, [ + 'type' => 'required', + 'sku' => 'required', + 'attribute_family_name' => 'required' + ]); + + if ( $validateProduct->fails() ) { + $errors = $validateProduct->errors()->getMessages(); + + foreach($errors as $key => $error) { + $recordCount = (int)$loopCount + (int)1; + + $errorToBeReturn[] = str_replace(".", "", $error[0]) . " for record " . $recordCount; + } + + request()->countOfStartedProfiles = $loopCount + 1; + + $productsUploaded = $loopCount - request()->errorCount; + + if (request()->numberOfCSVRecord != 0) { + $remainDataInCSV = (int)request()->totalNumberOfCSVRecord - (int)request()->countOfStartedProfiles; + } else { + $remainDataInCSV = 0; + } + + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => request()->countOfStartedProfiles, + 'error' => $errorToBeReturn, + ); + + return $dataToBeReturn; + } + + return null; + } catch(\EXception $e) { + } + } +} diff --git a/packages/Webkul/Bulkupload/src/Repositories/Products/SimpleProductRepository.php b/packages/Webkul/Bulkupload/src/Repositories/Products/SimpleProductRepository.php new file mode 100644 index 000000000..07f812efa --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Repositories/Products/SimpleProductRepository.php @@ -0,0 +1,452 @@ +importProductRepository = $importProductRepository; + + $this->categoryRepository = $categoryRepository; + + $this->attributeOptionRepository = $attributeOptionRepository; + + $this->productFlatRepository = $productFlatRepository; + + $this->productRepository = $productRepository; + + $this->productImageRepository = $productImageRepository; + + $this->attributeFamilyRepository = $attributeFamilyRepository; + + $this->helperRepository = $helperRepository; + + $this->inventorySourceRepository = $inventorySourceRepository; + } + + /* + * Specify Model class name + * + * @return mixed + */ + function model() + { + return 'Webkul\Product\Contracts\Product'; + } + + /** + * create & update simple-type product + * + * @param array $requestData + * @param array $imageZipName + * @param array $product + * + * @return mixed + */ + public function createProduct($requestData, $imageZipName, $product) + { + try { + $inventory = []; + + $dataFlowProfileRecord = $this->importProductRepository->findOneByField + ('data_flow_profile_id', $requestData['data_flow_profile_id']); + + $csvData = (new DataGridImport)->toArray($dataFlowProfileRecord->file_path)[0]; + + if ($requestData['totalNumberOfCSVRecord'] < 1000) { + $processCSVRecords = $requestData['totalNumberOfCSVRecord']/($requestData['totalNumberOfCSVRecord']/10); + } else { + $processCSVRecords = $requestData['totalNumberOfCSVRecord']/($requestData['totalNumberOfCSVRecord']/100); + } + + $uptoProcessCSVRecords = (int)$requestData['countOfStartedProfiles'] + 10; + + $processRecords = (int)$requestData['countOfStartedProfiles'] + (int)$requestData['numberOfCSVRecord']; + + + if ($requestData['numberOfCSVRecord'] > $processCSVRecords) { + for ($i = $requestData['countOfStartedProfiles']; $i < $uptoProcessCSVRecords; $i++) { + $invalidateProducts = $this->store($csvData[$i], $i, $dataFlowProfileRecord, $requestData, $imageZipName); + + if (isset($invalidateProducts) && !empty($invalidateProducts)) { + return $invalidateProducts; + } + } + } else if ($requestData['numberOfCSVRecord'] <= 10) { + for ($i = $requestData['countOfStartedProfiles']; $i < $processRecords; $i++) { + $invalidateProducts = $this->store($csvData[$i], $i, $dataFlowProfileRecord, $requestData, $imageZipName); + + if (isset($invalidateProducts) && !empty($invalidateProducts)) { + return $invalidateProducts; + } + } + } + + if ($requestData['numberOfCSVRecord'] > 10) { + $remainDataInCSV = (int)$requestData['numberOfCSVRecord'] - (int)$processCSVRecords; + } else { + $remainDataInCSV = 0; + + if($requestData['errorCount'] > 0) { + $uptoProcessCSVRecords = $requestData['totalNumberOfCSVRecord'] - $requestData['errorCount']; + } else { + $uptoProcessCSVRecords = $processRecords; + } + } + + $requestData['countOfStartedProfiles'] = $i; + + $dataToBeReturn = [ + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $uptoProcessCSVRecords, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + ]; + + return $dataToBeReturn; + } catch (\Exception $e) { + Log::error('simple create product log: '. $e->getMessage()); + + $categoryError = explode('[' ,$e->getMessage()); + $categorySlugError = explode(']' ,$e->getMessage()); + $requestData['countOfStartedProfiles'] = $i + 1; + $productsUploaded = $i - $requestData['errorCount']; + + if ($requestData['numberOfCSVRecord'] != 0) { + $remainDataInCSV = (int)$requestData['totalNumberOfCSVRecord'] - (int)$requestData['countOfStartedProfiles']; + } else { + $remainDataInCSV = 0; + } + + if ($categoryError[0] == "No query results for model ") { + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => "Invalid Category Slug: " . $categorySlugError[1], + ); + $categoryError[0] = null; + } else if (isset($e->errorInfo)) { + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => $e->errorInfo[2], + ); + } else { + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => $e->getMessage(), + ); + } + + return $dataToBeReturn; + } + } + + /** + * function to store product + * + * @param array $csvData + * @param integer $i + * @param \Webkul\Bulkupload\Contracts\ImportProduct $dataFlowProfileRecord + * @param array $requestData + * @param array $imageZipName + * + * @return mixed + */ + public function store($csvData, $i, $dataFlowProfileRecord, $requestData, $imageZipName) + { + try { + $createValidation = $this->helperRepository->createProductValidation($csvData, $i); + + if (isset($createValidation)) { + return $createValidation; + } + + $productFlatData = $this->productFlatRepository->findWhere(['sku' => $csvData['sku'], 'url_key' => $csvData['url_key']])->first(); + + $productData = $this->productRepository->findWhere(['sku' => $csvData['sku']])->first(); + + $attributeFamilyData = $this->attributeFamilyRepository->findOneByfield(['name' => $csvData['attribute_family_name']]); + + if (! isset($productFlatData) && empty($productFlatData)) { + $data['type'] = $csvData['type']; + $data['attribute_family_id'] = $attributeFamilyData->id; + $data['sku'] = $csvData['sku']; + + $simpleproductData = $this->productRepository->create($data); + } else { + $simpleproductData = $productData; + } + + unset($data); + $data = []; + $attributeCode = []; + $attributeValue = []; + + //default attributes + foreach ($simpleproductData->getTypeInstance()->getEditableAttributes()->toArray() as $key => $value) { + $attributeOptionArray = array(); + $searchIndex = strtolower($value['code']); + + if (array_key_exists($searchIndex, $csvData)) { + + if (is_null($csvData[$searchIndex])) { + continue; + } + + array_push($attributeCode, $searchIndex); + + if ($value['type'] == "select") { + $attributeOption = $this->attributeOptionRepository->findOneByField(['admin_name' => $csvData[$searchIndex]]); + + array_push($attributeValue, $attributeOption['id']); + } else if ($value['type'] == "checkbox") { + $attributeOption = $this->attributeOptionRepository->findOneByField(['attribute_id' => $value['id'], 'admin_name' => $csvData[$searchIndex]]); + + array_push($attributeOptionArray, $attributeOption['id']); + + array_push($attributeValue, $attributeOptionArray); + unset($attributeOptionArray); + } else { + array_push($attributeValue, $csvData[$searchIndex]); + } + + $data = array_combine($attributeCode, $attributeValue); + } + } + + $data['dataFlowProfileRecordId'] = $dataFlowProfileRecord->id; + + $inventorySource = $csvData['inventory_sources']; + $inventoryCode = explode(',', $inventorySource); + + foreach ($inventoryCode as $key => $value) { + $inventoryId = $this->inventorySourceRepository->findOneByfield(['code' => trim($value)])->pluck('id')->toArray(); + } + + $inventoryData[] = (string)$csvData['inventories']; + + foreach ($inventoryData as $key => $d) { + $inventoryQuantity = explode(',', trim($d)); + + if (count($inventoryId) != count($inventoryQuantity)) { + array_push($inventoryQuantity, "0"); + } + + $inventory = array_combine($inventoryId, $inventoryQuantity); + } + + $data['inventories'] = $inventory; + + $categoryData = explode(',', $csvData['categories_slug']); + + if (is_null($csvData['categories_slug']) || empty($csvData['categories_slug'])) { + $categoryID = $this->categoryRepository->findBySlugOrFail('root')->id; + } else { + foreach ($categoryData as $key => $value) { + $categoryID[$key] = $this->categoryRepository->findBySlugOrFail($categoryData[$key])->id; + } + } + + $data['categories'] = $categoryID; + $data['channel'] = core()->getCurrentChannel()->code; + + $dataProfile = app('Webkul\Bulkupload\Repositories\DataFlowProfileRepository')->findOneByfield(['id' => $data['dataFlowProfileRecordId']]); + + $data['locale'] = $dataProfile->locale_code; + + //customerGroupPricing + if (isset($csvData['customer_group_prices']) && ! empty($csvData['customer_group_prices'])) { + $data['customer_group_prices'] = json_decode($csvData['customer_group_prices'], true); + app(ProductCustomerGroupPriceRepository::class)->saveCustomerGroupPrices($data, $simpleproductData); + } + + //Product Images + $individualProductimages = explode(',', $csvData['images']); + + if (isset($imageZipName)) { + $images = Storage::disk('local')->files('public/imported-products/extracted-images/admin/'.$dataFlowProfileRecord->id.'/'.$imageZipName['dirname'].'/'); + + foreach ($images as $imageArraykey => $imagePath) { + $imageName = explode('/', $imagePath); + + if (in_array(last($imageName), preg_replace('/[\'"]/', '',$individualProductimages))) { + $data['images'][$imageArraykey] = $imagePath; + } + } + } else if (isset($csvData['images'])) { + foreach ($individualProductimages as $imageArraykey => $imageURL) + { + if (filter_var(trim($imageURL), FILTER_VALIDATE_URL)) { + $imagePath = storage_path('app/public/imported-products/extracted-images/admin/'.$dataFlowProfileRecord->id); + + if (!file_exists($imagePath)) { + mkdir($imagePath, 0777, true); + } + + $imageFile = $imagePath.'/'.basename($imageURL); + + file_put_contents($imageFile, file_get_contents(trim($imageURL))); + + $data['images'][$imageArraykey] = $imageFile; + } + } + } + + $returnRules = $this->helperRepository->validateCSV($requestData['data_flow_profile_id'], $data, $dataFlowProfileRecord, $simpleproductData); + + $csvValidator = Validator::make($data, $returnRules); + + if ($csvValidator->fails()) { + $errors = $csvValidator->errors()->getMessages(); + + $this->helperRepository->deleteProductIfNotValidated($simpleproductData->id); + + foreach($errors as $key => $error) { + if ($error[0] == "The url key has already been taken.") { + $errorToBeReturn[] = "The url key " . $data['url_key'] . " has already been taken"; + } else { + $errorToBeReturn[] = str_replace(".", "", $error[0]). " for sku " . $data['sku']; + } + } + + $requestData['countOfStartedProfiles'] = $i + 1; + + $productsUploaded = $i - $requestData['errorCount']; + + if ($requestData['numberOfCSVRecord'] != 0) { + $remainDataInCSV = (int)$requestData['totalNumberOfCSVRecord'] - (int)$requestData['countOfStartedProfiles']; + } else { + $remainDataInCSV = 0; + } + + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => $errorToBeReturn, + ); + + return $dataToBeReturn; + } + + $configSimpleProductAttributeStore = $this->productRepository->update($data, $simpleproductData->id); + + if (isset($imageZipName)) { + $this->productImageRepository->bulkuploadImages($data, $simpleproductData, $imageZipName); + } else if (isset($csvData['images'])) { + $this->productImageRepository->bulkuploadImages($data, $simpleproductData, $imageZipName = null); + } + } catch(\Exception $e) { + \Log::error('simple product store function'. $e->getMessage()); + } + } +} \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Repositories/Products/VirtualProductRepository.php b/packages/Webkul/Bulkupload/src/Repositories/Products/VirtualProductRepository.php new file mode 100644 index 000000000..852b97352 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Repositories/Products/VirtualProductRepository.php @@ -0,0 +1,440 @@ +importProductRepository = $importProductRepository; + + $this->categoryRepository = $categoryRepository; + + $this->productFlatRepository = $productFlatRepository; + + $this->productRepository = $productRepository; + + $this->productImageRepository = $productImageRepository; + + $this->attributeFamilyRepository = $attributeFamilyRepository; + + $this->helperRepository = $helperRepository; + + $this->attributeOptionRepository = $attributeOptionRepository; + + $this->inventorySourceRepository = $inventorySourceRepository; + } + + /* + * Specify Model class name + * + * @return mixed + */ + function model() + { + return 'Webkul\Product\Contracts\Product'; + } + + /** + * create & update virtual-type product + * + * @param array $requestData + * @param array $imageZipName + * + * @return mixed + */ + public function createProduct($requestData, $imageZipName) + { + try { + $inventory = []; + + $dataFlowProfileRecord = $this->importProductRepository->findOneByField + ('data_flow_profile_id', $requestData['data_flow_profile_id']); + + $csvData = (new DataGridImport)->toArray($dataFlowProfileRecord->file_path)[0]; + + if ($requestData['totalNumberOfCSVRecord'] < 1000) { + $processCSVRecords = $requestData['totalNumberOfCSVRecord']/($requestData['totalNumberOfCSVRecord']/10); + } else { + $processCSVRecords = $requestData['totalNumberOfCSVRecord']/($requestData['totalNumberOfCSVRecord']/100); + } + + $uptoProcessCSVRecords = (int)$requestData['countOfStartedProfiles'] + 10; + $processRecords = (int)$requestData['countOfStartedProfiles'] + (int)$requestData['numberOfCSVRecord']; + + if ($requestData['numberOfCSVRecord'] > $processCSVRecords) { + for ($i = $requestData['countOfStartedProfiles']; $i < $uptoProcessCSVRecords; $i++) { + $invalidateProducts = $this->store($csvData[$i], $i, $dataFlowProfileRecord, $requestData, $imageZipName); + + if (isset($invalidateProducts) && !empty($invalidateProducts)) { + return $invalidateProducts; + } + } + } else if ($requestData['numberOfCSVRecord'] <= 10) { + for ($i = $requestData['countOfStartedProfiles']; $i < $processRecords; $i++) { + $invalidateProducts = $this->store($csvData[$i], $i, $dataFlowProfileRecord, $requestData, $imageZipName); + + if (isset($invalidateProducts) && !empty($invalidateProducts)) { + return $invalidateProducts; + } + } + } + + if ($requestData['numberOfCSVRecord'] > 10) { + $remainDataInCSV = (int)$requestData['numberOfCSVRecord'] - (int)$processCSVRecords; + } else { + $remainDataInCSV = 0; + + if($requestData['errorCount'] > 0) { + $uptoProcessCSVRecords = $requestData['totalNumberOfCSVRecord'] - $requestData['errorCount']; + } else { + $uptoProcessCSVRecords = $processRecords; + } + } + + $requestData['countOfStartedProfiles'] = $i; + + $dataToBeReturn = [ + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $uptoProcessCSVRecords, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + ]; + + return $dataToBeReturn; + } catch(\Exception $e) { + Log::error('virtual create product log: '. $e->getMessage()); + + $categoryError = explode('[' ,$e->getMessage()); + $categorySlugError = explode(']' ,$e->getMessage()); + $requestData['countOfStartedProfiles'] = $i + 1; + $productsUploaded = $i - $requestData['errorCount']; + + if ($requestData['numberOfCSVRecord'] != 0) { + $remainDataInCSV = (int)$requestData['totalNumberOfCSVRecord'] - (int)$requestData['countOfStartedProfiles']; + } else { + $remainDataInCSV = 0; + } + + if ($categoryError[0] == "No query results for model ") { + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => "Invalid Category Slug: " . $categorySlugError[1], + ); + $categoryError[0] = null; + } else if (isset($e->errorInfo)) { + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => $e->errorInfo[2], + ); + } else { + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => $e->getMessage(), + ); + } + + return $dataToBeReturn; + } + } + + /** + * function to store product + * + * @param array $csvData + * @param integer $i + * @param \Webkul\Bulkupload\Contracts\ImportProduct $dataFlowProfileRecord + * @param array $requestData + * @param array $imageZipName + * + * @return mixed + */ + public function store($csvData, $i, $dataFlowProfileRecord, $requestData, $imageZipName) + { + try { + $createValidation = $this->helperRepository->createProductValidation($csvData, $i); + + if (isset($createValidation)) { + return $createValidation; + } + + $productFlatData = $this->productFlatRepository->findWhere(['sku' => $csvData['sku'], 'url_key' => $csvData['url_key']])->first(); + + $productData = $this->productRepository->findWhere(['sku' => $csvData['sku']])->first(); + + $attributeFamilyData = $this->attributeFamilyRepository->findOneByfield(['name' => $csvData['attribute_family_name']]); + + if (! isset($productFlatData) && empty($productFlatData)) { + $data['type'] = $csvData['type']; + $data['attribute_family_id'] = $attributeFamilyData->id; + $data['sku'] = $csvData['sku']; + + $virtualProductData = $this->productRepository->create($data); + } else { + $virtualProductData = $productData; + } + + unset($data); + $data = []; + $attributeCode = []; + $attributeValue = []; + + //default attributes + foreach ($virtualProductData->getTypeInstance()->getEditableAttributes()->toArray() as $key => $value) { + $searchIndex = $value['code']; + if (array_key_exists($searchIndex, $csvData)) { + if (is_null($csvData[$searchIndex])) { + continue; + } + + array_push($attributeCode, $searchIndex); + + if ($searchIndex == "color" || $searchIndex == "size" || $searchIndex == "brand") { + $attributeOption = $this->attributeOptionRepository->findOneByField(['admin_name' => ucwords($csvData[$searchIndex])]); + + array_push($attributeValue, $attributeOption['id']); + } else { + array_push($attributeValue, $csvData[$searchIndex]); + } + + $data = array_combine($attributeCode, $attributeValue); + } + } + + $data['dataFlowProfileRecordId'] = $dataFlowProfileRecord->id; + + $inventorySource = $csvData['inventory_sources']; + $inventoryCode = explode(',', $inventorySource); + + foreach ($inventoryCode as $key => $value) { + $inventoryId = $this->inventorySourceRepository->findOneByfield(['code' => trim($value)])->pluck('id')->toArray(); + } + + $inventoryData[] = (string)$csvData['inventories']; + + foreach ($inventoryData as $key => $d) { + $inventoryQuantity = explode(',', trim($d)); + + if (count($inventoryId) != count($inventoryQuantity)) { + array_push($inventoryQuantity, "0"); + } + + $inventory = array_combine($inventoryId, $inventoryQuantity); + } + + $data['inventories'] = $inventory; + + $categoryData = explode(',', $csvData['categories_slug']); + + if (is_null($csvData['categories_slug']) || empty($csvData['categories_slug'])) { + $categoryID = $this->categoryRepository->findBySlugOrFail('root')->id; + } else { + foreach ($categoryData as $key => $value) { + $categoryID[$key] = $this->categoryRepository->findBySlugOrFail($categoryData[$key])->id; + } + } + + $data['categories'] = $categoryID; + $data['channel'] = core()->getCurrentChannel()->code; + + $dataProfile = app('Webkul\Bulkupload\Repositories\DataFlowProfileRepository')->findOneByfield(['id' => $data['dataFlowProfileRecordId']]); + $data['locale'] = $dataProfile->locale_code; + + //customerGroupPricing + if (isset($csvData['customer_group_prices']) && ! empty($csvData['customer_group_prices'])) { + $data['customer_group_prices'] = json_decode($csvData['customer_group_prices'], true); + app(ProductCustomerGroupPriceRepository::class)->saveCustomerGroupPrices($data, $simpleproductData); + } + + //Product Images + $individualProductimages = explode(',', $csvData['images']); + + if (isset($imageZipName)) { + $images = Storage::disk('local')->files('public/imported-products/extracted-images/admin/'.$dataFlowProfileRecord->id.'/'.$imageZipName['dirname'].'/'); + + foreach ($images as $imageArraykey => $imagePath) { + $imageName = explode('/', $imagePath); + + if (in_array(last($imageName), preg_replace('/[\'"]/', '',$individualProductimages))) { + $data['images'][$imageArraykey] = $imagePath; + } + } + } else if (isset($csvData['images'])) { + foreach ($individualProductimages as $imageArraykey => $imageURL) + { + if (filter_var(trim($imageURL), FILTER_VALIDATE_URL)) { + $imagePath = storage_path('app/public/imported-products/extracted-images/admin/'.$dataFlowProfileRecord->id); + + if (!file_exists($imagePath)) { + mkdir($imagePath, 0777, true); + } + + $imageFile = $imagePath.'/'.basename($imageURL); + + file_put_contents($imageFile, file_get_contents(trim($imageURL))); + + $data['images'][$imageArraykey] = $imageFile; + } + } + } + + //to check validation + $validationRules = $this->helperRepository->validateCSV($requestData['data_flow_profile_id'], $data, $dataFlowProfileRecord, $virtualProductData); + + $csvValidator = Validator::make($data, $validationRules); + + if ($csvValidator->fails()) { + $errors = $csvValidator->errors()->getMessages(); + + $this->helperRepository->deleteProductIfNotValidated($virtualProductData->id); + + foreach($errors as $key => $error) { + if ($error[0] == "The url key has already been taken.") { + $errorToBeReturn[] = "The url key " . $data['url_key'] . " has already been taken"; + } else { + $errorToBeReturn[] = str_replace(".", "", $error[0]) . " for sku " . $data['sku']; + } + } + + $requestData['countOfStartedProfiles'] = $i + 1; + + $productsUploaded = $i - $requestData['errorCount']; + + if ($requestData['numberOfCSVRecord'] != 0) { + $remainDataInCSV = (int)$requestData['totalNumberOfCSVRecord'] - (int)$requestData['countOfStartedProfiles']; + } else { + $remainDataInCSV = 0; + } + + $dataToBeReturn = array( + 'remainDataInCSV' => $remainDataInCSV, + 'productsUploaded' => $productsUploaded, + 'countOfStartedProfiles' => $requestData['countOfStartedProfiles'], + 'error' => $errorToBeReturn, + ); + + return $dataToBeReturn; + } + + $this->productRepository->update($data, $virtualProductData->id); + + if (isset($imageZipName)) { + $this->productImageRepository->bulkuploadImages($data, $virtualProductData, $imageZipName); + } else if (isset($csvData['images'])) { + $this->productImageRepository->bulkuploadImages($data, $virtualProductData, $imageZipName = null); + } + } catch(\Exception $e) { + \Log::error('virtual product store function'. $e->getMessage()); + } + } +} \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Resources/assets/images/Icon-Bulk-Upload-Active.svg b/packages/Webkul/Bulkupload/src/Resources/assets/images/Icon-Bulk-Upload-Active.svg new file mode 100644 index 000000000..db0b09c72 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Resources/assets/images/Icon-Bulk-Upload-Active.svg @@ -0,0 +1,16 @@ + + + + Icon-Bulk-Upload-Active + Created with Sketch. + + + + + + + + + + + \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Resources/assets/images/Icon-Bulk-Upload.svg b/packages/Webkul/Bulkupload/src/Resources/assets/images/Icon-Bulk-Upload.svg new file mode 100644 index 000000000..e987ea4e0 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Resources/assets/images/Icon-Bulk-Upload.svg @@ -0,0 +1,16 @@ + + + + Icon-Bulk-Upload + Created with Sketch. + + + + + + + + + + + \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Resources/assets/images/Icon-Crossed.svg b/packages/Webkul/Bulkupload/src/Resources/assets/images/Icon-Crossed.svg new file mode 100644 index 000000000..038759b1b --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Resources/assets/images/Icon-Crossed.svg @@ -0,0 +1,10 @@ + + + + Icon-Crossed + Created with Sketch. + + + + + \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Resources/assets/images/check-accent.svg b/packages/Webkul/Bulkupload/src/Resources/assets/images/check-accent.svg new file mode 100644 index 000000000..8334c776f --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Resources/assets/images/check-accent.svg @@ -0,0 +1,10 @@ + + + + Check-Accent + Created with Sketch. + + + + + \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Resources/assets/images/cross-accent.svg b/packages/Webkul/Bulkupload/src/Resources/assets/images/cross-accent.svg new file mode 100644 index 000000000..be351129d --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Resources/assets/images/cross-accent.svg @@ -0,0 +1,11 @@ + + + + Cross-Accent + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Resources/assets/images/finish.svg b/packages/Webkul/Bulkupload/src/Resources/assets/images/finish.svg new file mode 100644 index 000000000..2401834e4 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Resources/assets/images/finish.svg @@ -0,0 +1,20 @@ + + + + tab/heading/icon/finish + Created with Sketch. + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Resources/assets/js/app.js b/packages/Webkul/Bulkupload/src/Resources/assets/js/app.js new file mode 100644 index 000000000..205c76683 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Resources/assets/js/app.js @@ -0,0 +1,8 @@ +window.Vue = require("vue"); +window.axios = require("axios"); + +Vue.prototype.$http = axios; + +window.eventBus = new Vue(); + +Vue.component("demo", require("./components/demo")); diff --git a/packages/Webkul/Bulkupload/src/Resources/assets/sass/admin.scss b/packages/Webkul/Bulkupload/src/Resources/assets/sass/admin.scss new file mode 100644 index 000000000..6eeb5d398 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Resources/assets/sass/admin.scss @@ -0,0 +1,52 @@ +.bulk-upload-icon { + width: 48px; + height: 48px; + display: inline-block; + background-size: cover; + background-image: url("../images/Icon-Bulk-Upload.svg"); +} + +.active { + .bulk-upload-icon { + background-image: url("../images/Icon-Bulk-Upload-Active.svg"); + } + + &.bulk-upload-icon { + background-image: url("../images/Icon-Bulk-Upload-Active.svg"); + } +} + +progress { + margin: 15px 0px 15px 34px; + width: 70%; + height: 15px; +} + +.icon { + background-size: cover; + display: inline-block; + + &.check-accent { + background-image: url("../images/check-accent.svg"); + width: 22px; + height: 18px; + } + + &.icon-crossed { + width: 18px; + height: 15px; + background-image: url("../images/Icon-Crossed.svg"); + } + + &.cross-accent { + width: 22px; + height: 18px; + background-image: url("../images/cross-accent.svg"); + } + + &.finish-icon { + width: 22px; + height: 18px; + background-image: url("../images/finish.svg"); + } +} diff --git a/packages/Webkul/Bulkupload/src/Resources/assets/sass/app.scss b/packages/Webkul/Bulkupload/src/Resources/assets/sass/app.scss new file mode 100644 index 000000000..6fa0b4572 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Resources/assets/sass/app.scss @@ -0,0 +1,52 @@ +.bulk-upload-icon { + width: 48px; + height: 48px; + display: inline-block; + background-size: cover; + background-image: url("../images/Icon-Bulk-Upload.svg"); +} + +.active { + .bulk-upload-icon { + width: 48px; + height: 48px; + display: inline-block; + background-size: cover; + background-image: url("../images/Icon-Bulk-Upload-Active.svg"); + } +} + +progress { + margin: 15px 0px 15px 34px; + width: 70%; + height: 15px; +} + +.icon { + background-size: cover; + display: inline-block; + + &.check-accent { + width: 22px; + height: 18px; + background-image: url("../images/check-accent.svg"); + } + + &.icon-crossed { + width: 18px; + height: 15px; + background-image: url("../images/Icon-Crossed.svg"); + } + + &.cross-accent { + width: 22px; + height: 18px; + background-image: url("../images/cross-accent.svg"); + } + + &.finish-icon { + width: 22px; + height: 18px; + background-image: url("../images/finish.svg"); + } +} \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Resources/lang/ar/app.php b/packages/Webkul/Bulkupload/src/Resources/lang/ar/app.php new file mode 100644 index 000000000..0e030c6d1 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Resources/lang/ar/app.php @@ -0,0 +1,60 @@ + [ + 'bulk-upload' => [ + 'index' => 'Bulkupload', + 'manage-bulk-upload' => 'Manage Bulk Upload', + + 'data-flow-profile' => [ + 'index' => 'Data Flow Profile', + 'add-profile' => 'Add Profile', + 'grid' => 'Profile Grid', + 'name' => 'Name', + + 'data-grid' => [ + 'created-at' => 'Created At' + ] + ], + + 'run-profile' => [ + 'index' => 'Run Profile', + 'select-file' => 'Select File', + 'please-select' => 'Please Select', + 'run' => 'Run', + 'profile-execution' => 'Starting profile execution, please wait...', + 'error-count' => 'Number of errors while product uploading', + 'error-in-product' => 'Error while product uploading', + 'warning' => 'Warning: Please do not close the window during importing data', + 'products-uploaded' => 'Products Uploaded', + 'finish' => 'Finished Profile Execution', + ], + + 'upload-files' => [ + 'index' => 'Upload Files', + 'download-sample' => 'Download Samples', + 'download' => 'Download', + 'csv-file' => 'Sample :filetype CSV File', + 'xls-file' => 'Sample :filetype XLS File', + 'import-products' => 'Import Products', + 'is-downloadable' => 'Is downloadable have files', + 'sample-links' => 'Is Links have samples', + 'sample-available' => 'Is Samples available', + 'upload-link-files' => 'Upload Link Files', + 'upload-link-sample-files' => 'Upload Link Sample Files', + 'upload-sample-files' => 'Upload Sample Files', + 'file' => 'CSV/XLS/XLSX file', + 'image' => 'Image Zip file', + 'save' => 'Save' + ], + + 'messages' => [ + 'profile-saved' => 'Profile added successfully', + 'profile-deleted' => 'Profile deleted successfully', + 'file-format-error' => 'Invalid File Extension', + 'update-profile' => 'Profile updated successfully', + 'data-profile-not-selected' => 'Data Flow Profile not selected', + ] + ], + ], +]; \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Resources/lang/en/app.php b/packages/Webkul/Bulkupload/src/Resources/lang/en/app.php new file mode 100644 index 000000000..09ba67a20 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Resources/lang/en/app.php @@ -0,0 +1,70 @@ + [ + 'system' => [ + 'bulkupload' => 'Bulk-Upload Product', + 'settings' => 'Settings', + 'general' => 'General', + 'status' => 'Status', + ], + + 'bulk-upload' => [ + 'index' => 'Bulkupload', + 'manage-bulk-upload' => 'Manage Bulk Upload', + + 'data-flow-profile' => [ + 'index' => 'Data Flow Profile', + 'add-profile' => 'Add Profile', + 'grid' => 'Profile Grid', + 'name' => 'Name', + 'edit-profile' => 'Edit Profile', + 'update-profile' => 'Update', + + 'data-grid' => [ + 'created-at' => 'Created At', + 'locale_code' => 'Locale code' + ] + ], + + 'run-profile' => [ + 'index' => 'Run Profile', + 'select-file' => 'Select File', + 'please-select' => 'Please Select', + 'run' => 'Run', + 'profile-execution' => 'Starting profile execution, please wait...', + 'error-count' => 'Number of errors while product uploading', + 'error-in-product' => 'Error while product uploading', + 'warning' => 'Warning: Please do not close the window during importing data', + 'products-uploaded' => 'Products Uploaded', + 'finish' => 'Finished Profile Execution', + ], + + 'upload-files' => [ + 'index' => 'Upload Files', + 'download-sample' => 'Download Samples', + 'download' => 'Download', + 'csv-file' => 'Sample :filetype CSV File', + 'xls-file' => 'Sample :filetype XLS File', + 'import-products' => 'Import Products', + 'is-downloadable' => 'Is downloadable have files', + 'sample-links' => 'Is Links have samples', + 'sample-available' => 'Is Samples available', + 'upload-link-files' => 'Upload Link Files', + 'upload-link-sample-files' => 'Upload Link Sample Files', + 'upload-sample-files' => 'Upload Sample Files', + 'file' => 'CSV/XLS/XLSX file', + 'image' => 'Image Zip file', + 'save' => 'Save' + ], + + 'messages' => [ + 'profile-saved' => 'Profile added successfully', + 'profile-deleted' => 'Profile deleted successfully', + 'file-format-error' => 'Invalid File Extension', + 'update-profile' => 'Profile updated successfully', + 'data-profile-not-selected' => 'Data Flow Profile not selected', + ] + ], + ], +]; \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Resources/lang/fa/app.php b/packages/Webkul/Bulkupload/src/Resources/lang/fa/app.php new file mode 100644 index 000000000..0e030c6d1 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Resources/lang/fa/app.php @@ -0,0 +1,60 @@ + [ + 'bulk-upload' => [ + 'index' => 'Bulkupload', + 'manage-bulk-upload' => 'Manage Bulk Upload', + + 'data-flow-profile' => [ + 'index' => 'Data Flow Profile', + 'add-profile' => 'Add Profile', + 'grid' => 'Profile Grid', + 'name' => 'Name', + + 'data-grid' => [ + 'created-at' => 'Created At' + ] + ], + + 'run-profile' => [ + 'index' => 'Run Profile', + 'select-file' => 'Select File', + 'please-select' => 'Please Select', + 'run' => 'Run', + 'profile-execution' => 'Starting profile execution, please wait...', + 'error-count' => 'Number of errors while product uploading', + 'error-in-product' => 'Error while product uploading', + 'warning' => 'Warning: Please do not close the window during importing data', + 'products-uploaded' => 'Products Uploaded', + 'finish' => 'Finished Profile Execution', + ], + + 'upload-files' => [ + 'index' => 'Upload Files', + 'download-sample' => 'Download Samples', + 'download' => 'Download', + 'csv-file' => 'Sample :filetype CSV File', + 'xls-file' => 'Sample :filetype XLS File', + 'import-products' => 'Import Products', + 'is-downloadable' => 'Is downloadable have files', + 'sample-links' => 'Is Links have samples', + 'sample-available' => 'Is Samples available', + 'upload-link-files' => 'Upload Link Files', + 'upload-link-sample-files' => 'Upload Link Sample Files', + 'upload-sample-files' => 'Upload Sample Files', + 'file' => 'CSV/XLS/XLSX file', + 'image' => 'Image Zip file', + 'save' => 'Save' + ], + + 'messages' => [ + 'profile-saved' => 'Profile added successfully', + 'profile-deleted' => 'Profile deleted successfully', + 'file-format-error' => 'Invalid File Extension', + 'update-profile' => 'Profile updated successfully', + 'data-profile-not-selected' => 'Data Flow Profile not selected', + ] + ], + ], +]; \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Resources/lang/nl/app.php b/packages/Webkul/Bulkupload/src/Resources/lang/nl/app.php new file mode 100644 index 000000000..0e030c6d1 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Resources/lang/nl/app.php @@ -0,0 +1,60 @@ + [ + 'bulk-upload' => [ + 'index' => 'Bulkupload', + 'manage-bulk-upload' => 'Manage Bulk Upload', + + 'data-flow-profile' => [ + 'index' => 'Data Flow Profile', + 'add-profile' => 'Add Profile', + 'grid' => 'Profile Grid', + 'name' => 'Name', + + 'data-grid' => [ + 'created-at' => 'Created At' + ] + ], + + 'run-profile' => [ + 'index' => 'Run Profile', + 'select-file' => 'Select File', + 'please-select' => 'Please Select', + 'run' => 'Run', + 'profile-execution' => 'Starting profile execution, please wait...', + 'error-count' => 'Number of errors while product uploading', + 'error-in-product' => 'Error while product uploading', + 'warning' => 'Warning: Please do not close the window during importing data', + 'products-uploaded' => 'Products Uploaded', + 'finish' => 'Finished Profile Execution', + ], + + 'upload-files' => [ + 'index' => 'Upload Files', + 'download-sample' => 'Download Samples', + 'download' => 'Download', + 'csv-file' => 'Sample :filetype CSV File', + 'xls-file' => 'Sample :filetype XLS File', + 'import-products' => 'Import Products', + 'is-downloadable' => 'Is downloadable have files', + 'sample-links' => 'Is Links have samples', + 'sample-available' => 'Is Samples available', + 'upload-link-files' => 'Upload Link Files', + 'upload-link-sample-files' => 'Upload Link Sample Files', + 'upload-sample-files' => 'Upload Sample Files', + 'file' => 'CSV/XLS/XLSX file', + 'image' => 'Image Zip file', + 'save' => 'Save' + ], + + 'messages' => [ + 'profile-saved' => 'Profile added successfully', + 'profile-deleted' => 'Profile deleted successfully', + 'file-format-error' => 'Invalid File Extension', + 'update-profile' => 'Profile updated successfully', + 'data-profile-not-selected' => 'Data Flow Profile not selected', + ] + ], + ], +]; \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Resources/lang/pt_BR/app.php b/packages/Webkul/Bulkupload/src/Resources/lang/pt_BR/app.php new file mode 100644 index 000000000..0e030c6d1 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Resources/lang/pt_BR/app.php @@ -0,0 +1,60 @@ + [ + 'bulk-upload' => [ + 'index' => 'Bulkupload', + 'manage-bulk-upload' => 'Manage Bulk Upload', + + 'data-flow-profile' => [ + 'index' => 'Data Flow Profile', + 'add-profile' => 'Add Profile', + 'grid' => 'Profile Grid', + 'name' => 'Name', + + 'data-grid' => [ + 'created-at' => 'Created At' + ] + ], + + 'run-profile' => [ + 'index' => 'Run Profile', + 'select-file' => 'Select File', + 'please-select' => 'Please Select', + 'run' => 'Run', + 'profile-execution' => 'Starting profile execution, please wait...', + 'error-count' => 'Number of errors while product uploading', + 'error-in-product' => 'Error while product uploading', + 'warning' => 'Warning: Please do not close the window during importing data', + 'products-uploaded' => 'Products Uploaded', + 'finish' => 'Finished Profile Execution', + ], + + 'upload-files' => [ + 'index' => 'Upload Files', + 'download-sample' => 'Download Samples', + 'download' => 'Download', + 'csv-file' => 'Sample :filetype CSV File', + 'xls-file' => 'Sample :filetype XLS File', + 'import-products' => 'Import Products', + 'is-downloadable' => 'Is downloadable have files', + 'sample-links' => 'Is Links have samples', + 'sample-available' => 'Is Samples available', + 'upload-link-files' => 'Upload Link Files', + 'upload-link-sample-files' => 'Upload Link Sample Files', + 'upload-sample-files' => 'Upload Sample Files', + 'file' => 'CSV/XLS/XLSX file', + 'image' => 'Image Zip file', + 'save' => 'Save' + ], + + 'messages' => [ + 'profile-saved' => 'Profile added successfully', + 'profile-deleted' => 'Profile deleted successfully', + 'file-format-error' => 'Invalid File Extension', + 'update-profile' => 'Profile updated successfully', + 'data-profile-not-selected' => 'Data Flow Profile not selected', + ] + ], + ], +]; \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Resources/views/admin/bulk-upload/data-flow-profile/edit.blade.php b/packages/Webkul/Bulkupload/src/Resources/views/admin/bulk-upload/data-flow-profile/edit.blade.php new file mode 100644 index 000000000..65ac3afa9 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Resources/views/admin/bulk-upload/data-flow-profile/edit.blade.php @@ -0,0 +1,54 @@ +@extends('admin::layouts.content') + +@section('page_title') +{{ __('bulkupload::app.admin.bulk-upload.bulk-upload-dataflow-profile') }} +@endsection + +@section('content') +
+
+ +
+
+

{{ __('bulkupload::app.admin.bulk-upload.data-flow-profile.edit-profile') }}

+
+ +
+ @csrf + input('family') ?> + +
+ + +
+ +
+ + + + + @if ($familyId) + + @endif + @{{ errors.first('attribute_family_id') }} +
+ +
+ +
+
+
+
+ +
+ {!! app('Webkul\Bulkupload\DataGrids\Admin\ProfileDataGrid')->render() !!} +
+
+
+@endsection diff --git a/packages/Webkul/Bulkupload/src/Resources/views/admin/bulk-upload/data-flow-profile/index.blade.php b/packages/Webkul/Bulkupload/src/Resources/views/admin/bulk-upload/data-flow-profile/index.blade.php new file mode 100644 index 000000000..c2d6b7659 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Resources/views/admin/bulk-upload/data-flow-profile/index.blade.php @@ -0,0 +1,74 @@ +@extends('admin::layouts.content') + +@section('page_title') + {{ __('bulkupload::app.admin.bulk-upload.data-flow-profile.index') }} +@stop + +@section('content') + + +
+
+

{{ __('bulkupload::app.admin.bulk-upload.data-flow-profile.add-profile') }}

+
+ +
+ @csrf + input('family') ?> + +
+ + + {{ $errors->first('name') }} +
+ +
+ + + + + @if ($familyId) + + @endif + + {{ $errors->first('attribute_family') }} +
+ +
+ + + + @{{ errors.first('locale_code') }} +
+ +
+ +
+
+
+ + +
+
+ {!! app('Webkul\Bulkupload\DataGrids\Admin\ProfileDataGrid')->render() !!} +
+
+
+@stop + diff --git a/packages/Webkul/Bulkupload/src/Resources/views/admin/bulk-upload/layouts/nav-aside.blade.php b/packages/Webkul/Bulkupload/src/Resources/views/admin/bulk-upload/layouts/nav-aside.blade.php new file mode 100644 index 000000000..e7f431d9e --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Resources/views/admin/bulk-upload/layouts/nav-aside.blade.php @@ -0,0 +1,41 @@ +
+ + {{-- button for collapsing aside nav --}} + + + +
diff --git a/packages/Webkul/Bulkupload/src/Resources/views/admin/bulk-upload/run-profile/index.blade.php b/packages/Webkul/Bulkupload/src/Resources/views/admin/bulk-upload/run-profile/index.blade.php new file mode 100644 index 000000000..33929b2be --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Resources/views/admin/bulk-upload/run-profile/index.blade.php @@ -0,0 +1,244 @@ +@extends('admin::layouts.content') + +@section('page_title') + {{ __('bulkupload::app.admin.bulk-upload.run-profile.index') }} +@stop + +@section('content') + + + +
+
+ +
+
+
+ +@stop + +@push('scripts') + + + + + + +@endpush \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Resources/views/admin/bulk-upload/style.blade.php b/packages/Webkul/Bulkupload/src/Resources/views/admin/bulk-upload/style.blade.php new file mode 100644 index 000000000..323086133 --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Resources/views/admin/bulk-upload/style.blade.php @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/src/Resources/views/admin/bulk-upload/upload-files/index.blade.php b/packages/Webkul/Bulkupload/src/Resources/views/admin/bulk-upload/upload-files/index.blade.php new file mode 100644 index 000000000..5252ba8ba --- /dev/null +++ b/packages/Webkul/Bulkupload/src/Resources/views/admin/bulk-upload/upload-files/index.blade.php @@ -0,0 +1,250 @@ +@extends('admin::layouts.content') + +@section('page_title') + {{ __('bulkupload::app.admin.bulk-upload.upload-files.index') }} +@stop + +@section('content') +
+ + + +
+
+
+ +
+
+
+
+ + + +
+
+
+ @csrf + input('family') ?> + +
+
+ + +
+ +
+ +
+ +
+ + + + + {{ $errors->first('file_path') }} +
+ +
+ + + + + {{ $errors->first('image_path') }} +
+
+ +
+ +
+
+
+
+
+
+@stop + +@push('scripts') + + + + + +@endpush \ No newline at end of file diff --git a/packages/Webkul/Bulkupload/webpack.mix.js b/packages/Webkul/Bulkupload/webpack.mix.js new file mode 100644 index 000000000..3dcb352c1 --- /dev/null +++ b/packages/Webkul/Bulkupload/webpack.mix.js @@ -0,0 +1,28 @@ +const { mix } = require("laravel-mix"); +require("laravel-mix-merge-manifest"); + +if (mix.inProduction()) { + var publicPath = "publishable/assets"; +} else { + var publicPath = "../../../public/vendor/webkul/admin/assets"; +} + +mix.setPublicPath(publicPath).mergeManifest(); +mix.disableNotifications(); + +mix + .copyDirectory( + __dirname + "/src/Resources/assets/images", + publicPath + "/images" + ) + .sass( + __dirname + "/src/Resources/assets/sass/admin.scss", + "css/bulk-admin.css" + ) + .options({ + processCssUrls: false, + }); + +if (mix.inProduction()) { + mix.version(); +}