├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── FUNDING.yml ├── ISSUE_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── PULL_REQUEST_TEMPLATE.md ├── .travis.yml ├── Api ├── ExtensionAttributesInterface.php └── QuoteHandlerInterface.php ├── Block └── Checkout │ └── Success.php ├── CHANGELOG.md ├── Helper └── Data.php ├── LICENSE.txt ├── Model ├── Config │ └── Source │ │ ├── AttributeQty.php │ │ └── Attributes.php ├── ExtensionAttributes.php └── QuoteHandler.php ├── Plugin └── SplitQuote.php ├── README.md ├── composer.json ├── etc ├── acl.xml ├── adminhtml │ └── system.xml ├── di.xml └── module.xml ├── registration.php └── view └── frontend ├── layout └── checkout_onepage_success.xml └── templates └── success.phtml /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at support@magestat.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Split Order extention for Magento 2 2 | 3 | This contribution model has contributors maintaining their own fork of the Split Order repository. 4 | The forked repository is then used to submit a request to the base repository to “pull” a set of changes. 5 | For more information on pull requests please refer to [GitHub Help](https://help.github.com/articles/about-pull-requests/). 6 | 7 | Contributions can take the form of new components or features, changes to existing features, tests, documentation (such as developer guides, user guides, examples, or specifications), bug fixes or optimizations. 8 | 9 | We will review all issues and contributions submitted by the community of developers in the first in, first out order. 10 | During the review we might require clarifications from the contributor. 11 | If there is no response from the contributor within two weeks, the pull request will be closed. 12 | 13 | ## Contribution requirements 14 | 15 | 1. Contributions must adhere to the [Magento coding standards](https://devdocs.magento.com/guides/v2.3/coding-standards/bk-coding-standards.html). 16 | 2. Pull requests (PRs) must be accompanied by a meaningful description of their purpose. Comprehensive descriptions increase the chances of a pull request being merged quickly and without additional clarification requests. 17 | 3. Commits must be accompanied by meaningful commit messages. Please see the [Pull Request Template](https://github.com/magestat/magento2-split-order/blob/master/.github/PULL_REQUEST_TEMPLATE.md) for more information. 18 | 4. PRs which include bug fixes must be accompanied with a step-by-step description of how to reproduce the bug. 19 | 3. PRs which include new logic or new features must be submitted along with Unit/integration test coverage 20 | 4. For larger features or changes, please [open an issue](https://github.com/magestat/magento2-split-order/issues) to discuss the proposed changes prior to development. This may prevent duplicate or unnecessary effort and allow other contributors to provide input. 21 | 5. All automated tests must pass (all builds on [Travis CI](https://travis-ci.org/magestat/magento2-split-order) must be green). 22 | 23 | ## Contribution process 24 | 25 | If you are a new GitHub user, we recommend that you create your own [free github account](https://github.com/signup/free). 26 | This will allow you to collaborate with our repository, fork the Split Order project and send pull requests. 27 | 28 | 1. Search current [listed issues](https://github.com/magestat/magento2-split-order/issues) (open or closed) for similar proposals of intended contribution before starting work on a new contribution. 29 | 2. Create and test your work. 30 | 3. Fork the Split Order repository and when you are ready to send us a pull request – follow the [Create A Pull Request instructions](https://github.com/magestat/magento2-split-order/blob/master/.github/PULL_REQUEST_TEMPLATE.md). 31 | 4. Once your contribution is received we will review the contribution and collaborate with you as needed. 32 | 33 | ## Code of Conduct 34 | 35 | Please note that this project is released with a Contributor Code of Conduct. We expect you to agree to its terms when participating in this project. 36 | The full text is available in the repository [Wiki](https://github.com/magestat/magento2-split-order/blob/develop/.github/CODE_OF_CONDUCT.md). 37 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: williankeller 2 | custom: ['https://paypal.me/williankeller'] 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 9 | 10 | ### Preconditions 11 | 15 | 1. 16 | 2. 17 | 18 | ### Steps to reproduce 19 | 23 | 1. 24 | 2. 25 | 3. 26 | 27 | ### Expected result 28 | 29 | 1. [Screenshots, logs or description] 30 | 31 | ### Actual result 32 | 33 | 1. [Screenshots, logs or description] 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Magento version** 8 | 9 | 10 | **Magento edition:** 11 | 12 | - [ ] Community Edition 13 | 14 | - [ ] Enterprise Edition 15 | 16 | 17 | **Describe the bug** 18 | 19 | 20 | **To Reproduce** 21 | Steps to reproduce the behavior: 22 | 1. Go to '...' 23 | 2. Click on '....' 24 | 3. Scroll down to '....' 25 | 4. See error 26 | 27 | **Expected behavior** 28 | 29 | 30 | **Screenshots** 31 | 32 | 33 | **Additional context** 34 | 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | ### Description 12 | 16 | 17 | ### Fixed Issues (if relevant) 18 | 22 | 1. magestat/magento2-split-order#: Issue title 23 | 2. ... 24 | 25 | ### Contribution checklist 26 | - [ ] Pull request has a meaningful description of its purpose 27 | - [ ] All commits are accompanied by meaningful commit messages 28 | - [ ] All new or changed code is covered with unit/integration tests (if applicable) 29 | - [ ] All automated tests passed successfully (all builds on Travis CI are green) 30 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | dist: trusty 2 | 3 | language: php 4 | 5 | php: 6 | - 7.1 7 | 8 | before_install: 9 | - composer config -g http-basic.repo.magento.com $MAGE_PUBLICKEY $MAGE_PRIVATEKEY 10 | - composer create-project magento/magento-coding-standard --stability=dev magento-coding-standard 11 | 12 | script: 13 | - magento-coding-standard/vendor/bin/phpcs --ignore=./vendor/*,./magento-coding-standard/*,./Test/* --standard=Magento2 --extensions=php,phtml,js . 14 | -------------------------------------------------------------------------------- /Api/ExtensionAttributesInterface.php: -------------------------------------------------------------------------------- 1 | checkoutSession = $checkoutSession; 43 | } 44 | 45 | /** 46 | * @return bool|array 47 | */ 48 | public function getOrderArray() 49 | { 50 | $splitOrders = $this->checkoutSession->getOrderIds(); 51 | $this->checkoutSession->unsOrderIds(); 52 | 53 | if (empty($splitOrders) || count($splitOrders) <= 1) { 54 | return false; 55 | } 56 | return $splitOrders; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | ### [1.0.7](https://github.com/magestat/magento2-split-order/releases/tag/1.0.7) - 02-17-2020 5 | #### Added 6 | - ACL for this module 7 | 8 | ### [1.0.6](https://github.com/magestat/magento2-split-order/releases/tag/1.0.6) - 05/31/2019 9 | #### Added 10 | - Split Order Based on the Product Stock Status. 11 | - Possibility to select Quantity attribute returnable value. 12 | 13 | #### Changed 14 | - Improved list of available attributes by removing unused ones. 15 | 16 | #### Fixed 17 | - Illegal offset type - QuoteHandler 18 | - Warning: count(): Parameter must be an array or an object that implements Countable 19 | 20 | 21 | ### [1.0.5](https://github.com/magestat/magento2-split-order/releases/tag/1.0.5) - 05/11/2019 22 | #### Fixed 23 | - Improved and fixed PHPDocs methods. 24 | - Fixed If multiple products in cart from same vendor giving wrong totals. 25 | 26 | ### [1.0.4](https://github.com/magestat/magento2-split-order/releases/tag/1.0.4) - 02/06/2019 27 | #### Added 28 | - Compatibility with PHP 7.2. 29 | 30 | 31 | ### [1.0.3](https://github.com/magestat/magento2-split-order/releases/tag/1.0.3) - 01/31/2019 32 | #### Added 33 | - Add Travis to validate code standard. 34 | 35 | #### Fixed 36 | - Change Session to be loaded by Proxy. 37 | - [#11](https://github.com/magestat/magento2-split-order/issues/11) Only single OrderID returned in `V1/guest-carts/:cartID/order` or `V1/carts/:cartID/order` PUT endpoints 38 | 39 | #### Changed 40 | - Improved module architecture & code structure. 41 | 42 | 43 | ### [1.0.2](https://github.com/magestat/magento2-split-order/releases/tag/1.0.2) - 08/21/2018 44 | #### Added 45 | - Option to select a product attribute to define order split. 46 | - Capability to split shipping totals into all orders or only one. 47 | 48 | #### Fixed 49 | - Split shipping totals into all orders or only one. 50 | - Set shipping to one order only. 51 | 52 | #### Changed 53 | - Improved module architecture. 54 | - Stable order submission. 55 | 56 | 57 | ### [1.0.1](https://github.com/magestat/magento2-split-order/releases/tag/1.0.1) - 07/20/2018 58 | #### Added 59 | - CMS module settings (Enable/Disable) 60 | 61 | #### Fixed 62 | - Compatibility with 2.1.* EE. 63 | 64 | #### Changed 65 | - Improved module architecture. 66 | - Stable order submission. 67 | - Expose methods to be intercepted. 68 | 69 | 70 | ### [1.0.0](https://github.com/magestat/magento2-split-order/releases/tag/1.0.0) - 02/15/2018 71 | #### Added 72 | - Release module. 73 | -------------------------------------------------------------------------------- /Helper/Data.php: -------------------------------------------------------------------------------- 1 | scopeConfig->isSetFlag( 28 | 'magestat_split_order/module/enabled', 29 | ScopeInterface::SCOPE_STORE, 30 | $storeId 31 | ); 32 | } 33 | 34 | /** 35 | * Get attributes to split. 36 | * 37 | * @param int $storeId 38 | * @return string 39 | */ 40 | public function getAttributes($storeId = null) 41 | { 42 | return $this->scopeConfig->getValue( 43 | 'magestat_split_order/options/attributes', 44 | ScopeInterface::SCOPE_STORE, 45 | $storeId 46 | ); 47 | } 48 | 49 | /** 50 | * Check if should split delivery. 51 | * 52 | * @param string $storeId 53 | * @return bool 54 | */ 55 | public function getShippingSplit($storeId = null) 56 | { 57 | return (bool) $this->scopeConfig->isSetFlag( 58 | 'magestat_split_order/options/shipping', 59 | ScopeInterface::SCOPE_STORE, 60 | $storeId 61 | ); 62 | } 63 | 64 | /** 65 | * Get which kind of attribute related with qty should be load. 66 | * 67 | * @param int $storeId 68 | * @return bool 69 | */ 70 | public function getQtyType($storeId = null) 71 | { 72 | return $this->scopeConfig->getValue( 73 | 'magestat_split_order/options/attribute_qty', 74 | ScopeInterface::SCOPE_STORE, 75 | $storeId 76 | ); 77 | } 78 | 79 | /** 80 | * If should apply out of stock if inventory is empty. 81 | * 82 | * @param int $storeId 83 | * @return string 84 | */ 85 | public function getBackorder($storeId = null) 86 | { 87 | return (bool) $this->scopeConfig->isSetFlag( 88 | 'magestat_split_order/options/qty_backorder', 89 | ScopeInterface::SCOPE_STORE, 90 | $storeId 91 | ); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | This Open Software License (the "License") applies to any original work of authorship (the "Original Work") whose owner (the "Licensor") has placed the following licensing notice adjacent to the copyright notice for the Original Work: 2 | 3 | Licensed under the Open Software License version 3.0 4 | 5 | 1) Grant of Copyright License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, for the duration of the copyright, to do the following: 6 | 7 | a) to reproduce the Original Work in copies, either alone or as part of a collective work; 8 | 9 | b) to translate, adapt, alter, transform, modify, or arrange the Original Work, thereby creating derivative works ("Derivative Works") based upon the Original Work; 10 | 11 | c) to distribute or communicate copies of the Original Work and Derivative Works to the public, with the proviso that copies of Original Work or Derivative Works that You distribute or communicate shall be licensed under this Open Software License; 12 | 13 | d) to perform the Original Work publicly; and 14 | 15 | e) to display the Original Work publicly. 16 | 17 | 2) Grant of Patent License. Licensor grants You a worldwide, royalty-free, non-exclusive, sublicensable license, under patent claims owned or controlled by the Licensor that are embodied in the Original Work as furnished by the Licensor, for the duration of the patents, to make, use, sell, offer for sale, have made, and import the Original Work and Derivative Works. 18 | 19 | 3) Grant of Source Code License. The term "Source Code" means the preferred form of the Original Work for making modifications to it and all available documentation describing how to modify the Original Work. Licensor agrees to provide a machine-readable copy of the Source Code of the Original Work along with each copy of the Original Work that Licensor distributes. Licensor reserves the right to satisfy this obligation by placing a machine-readable copy of the Source Code in an information repository reasonably calculated to permit inexpensive and convenient access by You for as long as Licensor continues to distribute the Original Work. 20 | 21 | 4) Exclusions From License Grant. Neither the names of Licensor, nor the names of any contributors to the Original Work, nor any of their trademarks or service marks, may be used to endorse or promote products derived from this Original Work without express prior permission of the Licensor. Except as expressly stated herein, nothing in this License grants any license to Licensor's trademarks, copyrights, patents, trade secrets or any other intellectual property. No patent license is granted to make, use, sell, offer for sale, have made, or import embodiments of any patent claims other than the licensed claims defined in Section 2. No license is granted to the trademarks of Licensor even if such marks are included in the Original Work. Nothing in this License shall be interpreted to prohibit Licensor from licensing under terms different from this License any Original Work that Licensor otherwise would have a right to license. 22 | 23 | 5) External Deployment. The term "External Deployment" means the use, distribution, or communication of the Original Work or Derivative Works in any way such that the Original Work or Derivative Works may be used by anyone other than You, whether those works are distributed or communicated to those persons or made available as an application intended for use over a network. As an express condition for the grants of license hereunder, You must treat any External Deployment by You of the Original Work or a Derivative Work as a distribution under section 1(c). 24 | 25 | 6) Attribution Rights. You must retain, in the Source Code of any Derivative Works that You create, all copyright, patent, or trademark notices from the Source Code of the Original Work, as well as any notices of licensing and any descriptive text identified therein as an "Attribution Notice." You must cause the Source Code for any Derivative Works that You create to carry a prominent Attribution Notice reasonably calculated to inform recipients that You have modified the Original Work. 26 | 27 | 7) Warranty of Provenance and Disclaimer of Warranty. Licensor warrants that the copyright in and to the Original Work and the patent rights granted herein by Licensor are owned by the Licensor or are sublicensed to You under the terms of this License with the permission of the contributor(s) of those copyrights and patent rights. Except as expressly stated in the immediately preceding sentence, the Original Work is provided under this License on an "AS IS" BASIS and WITHOUT WARRANTY, either express or implied, including, without limitation, the warranties of non-infringement, merchantability or fitness for a particular purpose. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential part of this License. No license to the Original Work is granted by this License except under this disclaimer. 28 | 29 | 8) Limitation of Liability. Under no circumstances and under no legal theory, whether in tort (including negligence), contract, or otherwise, shall the Licensor be liable to anyone for any indirect, special, incidental, or consequential damages of any character arising as a result of this License or the use of the Original Work including, without limitation, damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses. This limitation of liability shall not apply to the extent applicable law prohibits such limitation. 30 | 31 | 9) Acceptance and Termination. If, at any time, You expressly assented to this License, that assent indicates your clear and irrevocable acceptance of this License and all of its terms and conditions. If You distribute or communicate copies of the Original Work or a Derivative Work, You must make a reasonable effort under the circumstances to obtain the express assent of recipients to the terms of this License. This License conditions your rights to undertake the activities listed in Section 1, including your right to create Derivative Works based upon the Original Work, and doing so without honoring these terms and conditions is prohibited by copyright law and international treaty. Nothing in this License is intended to affect copyright exceptions and limitations (including "fair use" or "fair dealing"). This License shall terminate immediately and You may no longer exercise any of the rights granted to You by this License upon your failure to honor the conditions in Section 1(c). 32 | 33 | 10) Termination for Patent Action. This License shall terminate automatically and You may no longer exercise any of the rights granted to You by this License as of the date You commence an action, including a cross-claim or counterclaim, against Licensor or any licensee alleging that the Original Work infringes a patent. This termination provision shall not apply for an action alleging patent infringement by combinations of the Original Work with other software or hardware. 34 | 35 | 11) Jurisdiction, Venue and Governing Law. Any action or suit relating to this License may be brought only in the courts of a jurisdiction wherein the Licensor resides or in which Licensor conducts its primary business, and under the laws of that jurisdiction excluding its conflict-of-law provisions. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any use of the Original Work outside the scope of this License or after its termination shall be subject to the requirements and penalties of copyright or patent law in the appropriate jurisdiction. This section shall survive the termination of this License. 36 | 37 | 12) Attorneys' Fees. In any action to enforce the terms of this License or seeking damages relating thereto, the prevailing party shall be entitled to recover its costs and expenses, including, without limitation, reasonable attorneys' fees and costs incurred in connection with such action, including any appeal of such action. This section shall survive the termination of this License. 38 | 39 | 13) Miscellaneous. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. 40 | 41 | 14) Definition of "You" in This License. "You" throughout this License, whether in upper or lower case, means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with you. For purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. 42 | 43 | 15) Right to Use. You may use the Original Work in all ways not otherwise restricted or conditioned by this License or by law, and Licensor promises not to interfere with or be responsible for such uses by You. 44 | 45 | 16) Modification of This License. This License is Copyright © 2005 Lawrence Rosen. Permission is granted to copy, distribute, or communicate this License without modification. Nothing in this License permits You to modify this License as applied to the Original Work or to Derivative Works. However, You may modify the text of this License and copy, distribute or communicate your modified version (the "Modified License") and apply it to other original works of authorship subject to the following conditions: (i) You may not indicate in any way that your Modified License is the "Open Software License" or "OSL" and you may not use those names in the name of your Modified License; (ii) You must replace the notice specified in the first paragraph above with the notice "Licensed under " or with a notice of your own that is not confusingly similar to the notice in this License; and (iii) You may not claim that your original works are open source software unless your Modified License has been approved by Open Source Initiative (OSI) and You comply with its license review and certification process. -------------------------------------------------------------------------------- /Model/Config/Source/AttributeQty.php: -------------------------------------------------------------------------------- 1 | __('Stock Quantity (Inventory value)'), 21 | 'status' => __('Stock Status (In or Out of stock)') 22 | ]; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Model/Config/Source/Attributes.php: -------------------------------------------------------------------------------- 1 | collection = $collectionFactory; 54 | } 55 | 56 | /** 57 | * Get options as array 58 | * 59 | * @return array 60 | */ 61 | public function toOptionArray() 62 | { 63 | if ($this->options) { 64 | return $this->options; 65 | } 66 | $collection = $this->collection->create(); 67 | 68 | $attributes = []; 69 | foreach ($collection as $item) { 70 | if (empty($item->getFrontendLabel()) || in_array($item->getAttributeCode(), self::BLACK_LIST)) { 71 | continue; 72 | } 73 | $attributes[] = [ 74 | 'value' => $item->getAttributeCode(), 75 | 'label' => $item->getFrontendLabel() 76 | ]; 77 | } 78 | $this->options = $attributes; 79 | 80 | $options = $this->options; 81 | array_unshift($options, ['value' => '', 'label' => __('--Please Select--')]); 82 | 83 | return $options; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /Model/ExtensionAttributes.php: -------------------------------------------------------------------------------- 1 | helperData = $helperData; 26 | } 27 | 28 | /** 29 | * @inheritdoc 30 | */ 31 | public function loadValue($product, $attributeCode) 32 | { 33 | /** @var \Magento\Catalog\Api\Data\ProductExtensionInterface $attributes */ 34 | $attributes = $product->getExtensionAttributes(); 35 | if ($attributeCode === self::QUANTITY_AND_STOCK_STATUS) { 36 | return (string) $this->quantityAndStockStatus($attributes); 37 | } 38 | return false; 39 | } 40 | 41 | /** 42 | * @inheritdoc 43 | */ 44 | public function quantityAndStockStatus($attributes) 45 | { 46 | if ($this->helperData->getQtyType() === 'qty') { 47 | return $attributes->getStockItem()->getQty(); 48 | } 49 | if ($this->helperData->getBackorder() && $attributes->getStockItem()->getQty() < 1) { 50 | return 'out'; 51 | } 52 | return ($attributes->getStockItem()->getIsInStock()) ? 'in' : 'out'; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Model/QuoteHandler.php: -------------------------------------------------------------------------------- 1 | checkoutSession = $checkoutSession; 45 | $this->helperData = $helperData; 46 | $this->extensionAttributes = $extensionAttributes; 47 | } 48 | 49 | /** 50 | * @inheritdoc 51 | */ 52 | public function normalizeQuotes($quote) 53 | { 54 | if (!$this->helperData->isActive()) { 55 | return false; 56 | } 57 | $attributes = $this->helperData->getAttributes(); 58 | if (empty($attributes)) { 59 | return false; 60 | } 61 | $groups = []; 62 | 63 | /** @var \Magento\Quote\Model\Quote\Item $item */ 64 | foreach ($quote->getAllVisibleItems() as $item) { 65 | /** @var \Magento\Catalog\Model\Product $product */ 66 | $product = $item->getProduct(); 67 | 68 | $attribute = $this->getProductAttributes($product, $attributes); 69 | if ($attribute === false) { 70 | return false; 71 | } 72 | $groups[$attribute][] = $item; 73 | } 74 | // If order have more than one different attribute values. 75 | if (count($groups) > 1) { 76 | return $groups; 77 | } 78 | return false; 79 | } 80 | 81 | /** 82 | * @inheritdoc 83 | */ 84 | public function getProductAttributes($product, $attributeCode) 85 | { 86 | $extensionAttribute = $this->extensionAttributes->loadValue($product, $attributeCode); 87 | if ($extensionAttribute !== false) { 88 | return $extensionAttribute; 89 | } 90 | $attributeObject = $product->getResource()->getAttribute($attributeCode); 91 | 92 | $attributeValue = $attributeObject->getFrontend()->getValue($product); 93 | if ($attributeValue instanceof \Magento\Framework\Phrase) { 94 | return $attributeValue->__toString(); 95 | } 96 | return $attributeValue; 97 | } 98 | 99 | /** 100 | * @inheritdoc 101 | */ 102 | public function collectAddressesData($quote) 103 | { 104 | $billing = $quote->getBillingAddress()->getData(); 105 | unset($billing['id']); 106 | unset($billing['quote_id']); 107 | 108 | $shipping = $quote->getShippingAddress()->getData(); 109 | unset($shipping['id']); 110 | unset($shipping['quote_id']); 111 | 112 | return [ 113 | 'payment' => $quote->getPayment()->getMethod(), 114 | 'billing' => $billing, 115 | 'shipping' => $shipping 116 | ]; 117 | } 118 | 119 | /** 120 | * @inheritdoc 121 | */ 122 | public function setCustomerData($quote, $split) 123 | { 124 | $split->setStoreId($quote->getStoreId()); 125 | $split->setCustomer($quote->getCustomer()); 126 | $split->setCustomerIsGuest($quote->getCustomerIsGuest()); 127 | 128 | if ($quote->getCheckoutMethod() === CartManagementInterface::METHOD_GUEST) { 129 | $split->setCustomerId(null); 130 | $split->setCustomerEmail($quote->getBillingAddress()->getEmail()); 131 | $split->setCustomerIsGuest(true); 132 | $split->setCustomerGroupId(GroupInterface::NOT_LOGGED_IN_ID); 133 | } 134 | return $this; 135 | } 136 | 137 | /** 138 | * @inheritdoc 139 | */ 140 | public function populateQuote($quotes, $split, $items, $addresses, $payment) 141 | { 142 | $this->recollectTotal($quotes, $items, $split, $addresses); 143 | // Set payment method. 144 | $this->setPaymentMethod($split, $addresses['payment'], $payment); 145 | 146 | return $this; 147 | } 148 | 149 | /** 150 | * @inheritdoc 151 | */ 152 | public function recollectTotal($quotes, $items, $quote, $addresses) 153 | { 154 | $tax = 0.0; 155 | $discount = 0.0; 156 | $finalPrice = 0.0; 157 | 158 | foreach ($items as $item) { 159 | // Retrieve values. 160 | $tax += $item->getData('tax_amount'); 161 | $discount += $item->getData('discount_amount'); 162 | 163 | $finalPrice += ($item->getPrice() * $item->getQty()); 164 | } 165 | 166 | // Set addresses. 167 | $quote->getBillingAddress()->setData($addresses['billing']); 168 | $quote->getShippingAddress()->setData($addresses['shipping']); 169 | 170 | // Add shipping amount if product is not virtual. 171 | $shipping = $this->shippingAmount($quotes, $quote); 172 | 173 | // Recollect totals into the quote. 174 | foreach ($quote->getAllAddresses() as $address) { 175 | // Build grand total. 176 | $grandTotal = (($finalPrice + $shipping + $tax) - $discount); 177 | 178 | $address->setBaseSubtotal($finalPrice); 179 | $address->setSubtotal($finalPrice); 180 | $address->setDiscountAmount($discount); 181 | $address->setTaxAmount($tax); 182 | $address->setBaseTaxAmount($tax); 183 | $address->setBaseGrandTotal($grandTotal); 184 | $address->setGrandTotal($grandTotal); 185 | } 186 | return $this; 187 | } 188 | 189 | /** 190 | * @inheritdoc 191 | */ 192 | public function shippingAmount($quotes, $quote, $total = 0.0) 193 | { 194 | // Add shipping amount if product is not virtual. 195 | if ($quote->hasVirtualItems() === true) { 196 | return $total; 197 | } 198 | $shippingTotals = $quote->getShippingAddress()->getShippingAmount(); 199 | 200 | // If not, set shipping to one order only. 201 | if (!$this->helperData->getShippingSplit()) { 202 | static $process = 1; 203 | 204 | if ($process > 1) { 205 | // Set zero price to next orders. 206 | $quote->getShippingAddress()->setShippingAmount($total); 207 | return $total; 208 | } 209 | $process ++; 210 | 211 | return $shippingTotals; 212 | } 213 | if ($shippingTotals > 0) { 214 | // Divide shipping to each order. 215 | $total = (float) ($shippingTotals / count($quotes)); 216 | $quote->getShippingAddress()->setShippingAmount($total); 217 | } 218 | return $total; 219 | } 220 | 221 | /** 222 | * @inheritdoc 223 | */ 224 | public function setPaymentMethod($split, $payment, $paymentMethod) 225 | { 226 | $split->getPayment()->setMethod($payment); 227 | 228 | if ($paymentMethod) { 229 | $split->getPayment()->setQuote($split); 230 | $data = $paymentMethod->getData(); 231 | $split->getPayment()->importData($data); 232 | } 233 | return $this; 234 | } 235 | 236 | /** 237 | * @inheritdoc 238 | */ 239 | public function defineSessions($split, $order, $orderIds) 240 | { 241 | $this->checkoutSession->setLastQuoteId($split->getId()); 242 | $this->checkoutSession->setLastSuccessQuoteId($split->getId()); 243 | $this->checkoutSession->setLastOrderId($order->getId()); 244 | $this->checkoutSession->setLastRealOrderId($order->getIncrementId()); 245 | $this->checkoutSession->setLastOrderStatus($order->getStatus()); 246 | $this->checkoutSession->setOrderIds($orderIds); 247 | 248 | return $this; 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /Plugin/SplitQuote.php: -------------------------------------------------------------------------------- 1 | quoteRepository = $quoteRepository; 51 | $this->quoteFactory = $quoteFactory; 52 | $this->eventManager = $eventManager; 53 | $this->quoteHandler = $quoteHandler; 54 | } 55 | 56 | /** 57 | * Places an order for a specified cart. 58 | * 59 | * @param QuoteManagement $subject 60 | * @param callable $proceed 61 | * @param int $cartId 62 | * @param string $payment 63 | * @return mixed 64 | * @throws LocalizedException 65 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 66 | * @see \Magento\Quote\Api\CartManagementInterface 67 | */ 68 | public function aroundPlaceOrder(QuoteManagement $subject, callable $proceed, $cartId, $payment = null) 69 | { 70 | $currentQuote = $this->quoteRepository->getActive($cartId); 71 | 72 | // Separate all items in quote into new quotes. 73 | $quotes = $this->quoteHandler->normalizeQuotes($currentQuote); 74 | if (empty($quotes)) { 75 | return $result = array_values([($proceed($cartId, $payment))]); 76 | } 77 | // Collect list of data addresses. 78 | $addresses = $this->quoteHandler->collectAddressesData($currentQuote); 79 | 80 | /** @var \Magento\Sales\Api\Data\OrderInterface[] $orders */ 81 | $orders = []; 82 | $orderIds = []; 83 | foreach ($quotes as $items) { 84 | /** @var \Magento\Quote\Model\Quote $split */ 85 | $split = $this->quoteFactory->create(); 86 | 87 | // Set all customer definition data. 88 | $this->quoteHandler->setCustomerData($currentQuote, $split); 89 | $this->toSaveQuote($split); 90 | 91 | // Map quote items. 92 | foreach ($items as $item) { 93 | // Add item by item. 94 | $item->setId(null); 95 | $split->addItem($item); 96 | } 97 | $this->quoteHandler->populateQuote($quotes, $split, $items, $addresses, $payment); 98 | 99 | // Dispatch event as Magento standard once per each quote split. 100 | $this->eventManager->dispatch( 101 | 'checkout_submit_before', 102 | ['quote' => $split] 103 | ); 104 | 105 | $this->toSaveQuote($split); 106 | $order = $subject->submit($split); 107 | 108 | $orders[] = $order; 109 | $orderIds[$order->getId()] = $order->getIncrementId(); 110 | 111 | if (null == $order) { 112 | throw new LocalizedException(__('Please try to place the order again.')); 113 | } 114 | } 115 | $currentQuote->setIsActive(false); 116 | $this->toSaveQuote($currentQuote); 117 | 118 | $this->quoteHandler->defineSessions($split, $order, $orderIds); 119 | 120 | $this->eventManager->dispatch( 121 | 'checkout_submit_all_after', 122 | ['orders' => $orders, 'quote' => $currentQuote] 123 | ); 124 | return $this->getOrderKeys($orderIds); 125 | } 126 | 127 | /** 128 | * Save quote 129 | * 130 | * @param \Magento\Quote\Api\Data\CartInterface $quote 131 | * @return \Magestat\SplitOrder\Plugin\SplitQuote 132 | */ 133 | private function toSaveQuote($quote) 134 | { 135 | $this->quoteRepository->save($quote); 136 | 137 | return $this; 138 | } 139 | 140 | /** 141 | * @param array $orderIds 142 | * @return array 143 | */ 144 | private function getOrderKeys($orderIds) 145 | { 146 | $orderValues = []; 147 | foreach (array_keys($orderIds) as $orderKey) { 148 | $orderValues[] = (string) $orderKey; 149 | } 150 | return array_values($orderValues); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [DEPRECATED] Split Order for Magento 2 2 | 3 | This extension allows your Magento store to split the order into an order for each item in the cart. With different order IDs, customers can view all the order ids in their Order History and track each item separately. The Magento admin generate separate invoices and shipments for each splitted order. Shipping charges and tax are also split based on items. This extension Magento 2 default offline payment methods: Check / Money Order and Cash on Delivery. 4 | 5 | [![No Maintenance Intended](http://unmaintained.tech/badge.svg)](http://unmaintained.tech/) 6 | [![Build Status](https://travis-ci.org/magestat/magento2-split-order.svg?branch=develop)](https://travis-ci.org/magestat/magento2-split-order) [![Packagist](https://img.shields.io/packagist/v/magestat/module-split-order.svg)](https://packagist.org/packages/magestat/module-split-order) [![Downloads](https://img.shields.io/packagist/dt/magestat/module-split-order.svg)](https://packagist.org/packages/magestat/module-split-order) 7 | 8 | 9 | ## Installation 10 | 11 | ### Install via composer (recommended) 12 | 13 | Run the following command in Magento 2 root folder: 14 | ```sh 15 | composer require magestat/module-split-order 16 | ``` 17 | 18 | ### Using GIT clone 19 | 20 | Run the following command in Magento 2 root folder: 21 | ```sh 22 | git clone git@github.com:magestat/magento2-split-order.git app/code/Magestat/SplitOrder 23 | ``` 24 | 25 | ## 2. Activation 26 | 27 | Run the following command in Magento 2 root folder: 28 | ```sh 29 | php bin/magento module:enable Magestat_SplitOrder 30 | ``` 31 | ```sh 32 | php bin/magento setup:upgrade 33 | ``` 34 | 35 | Clear the caches: 36 | ```sh 37 | php bin/magento cache:clean 38 | ``` 39 | 40 | ## 3. Configuration 41 | 42 | 1. Go to **STORES** > **Configuration** > **MAGESTAT** > **Split Order**. 43 | 2. Select **Enabled** option to enable module. 44 | 3. Change the options selecting the attribute to split the order just like you want. 45 | 46 | ## Contribution 47 | 48 | Want to contribute to this extension? The quickest way is to open a [pull request on GitHub](https://help.github.com/articles/using-pull-requests). 49 | 50 | 51 | ## Support 52 | 53 | If you encounter any problems or bugs, please open an issue on [GitHub](https://github.com/magestat/magento2-split-order/issues). 54 | 55 | Need help setting up or want to customize this extension to meet your business needs? Please open an issue and if we like your idea we will add this feature for free. 56 | 57 | ## Known issues 58 | 59 | 1. [Doesn't work with Braintree, Paypal via Braintree, Paypal Express Checkout](https://github.com/magestat/magento2-split-order/issues/10) 60 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "magestat/module-split-order", 3 | "description": "This extension allows your Magento store to split the order into an order for each item in the cart.", 4 | "keywords": [ 5 | "magento 2", 6 | "magento extension", 7 | "split order", 8 | "magento quote", 9 | "split quote", 10 | "magento 2 split quote", 11 | "magento 2 split order" 12 | ], 13 | "type": "magento2-module", 14 | "license": [ 15 | "OSL-3.0" 16 | ], 17 | "version": "1.0.7", 18 | "support": { 19 | "issues": "https://github.com/magestat/magento2-split-order/issues/" 20 | }, 21 | "require-dev": { 22 | "phpmd/phpmd": "@stable", 23 | "phpspec/phpspec": "^2.5", 24 | "squizlabs/php_codesniffer": "3.0.1" 25 | }, 26 | "repositories": { 27 | "0": { 28 | "type": "composer", 29 | "url": "https://repo.magento.com/" 30 | } 31 | }, 32 | "autoload": { 33 | "files": [ 34 | "registration.php" 35 | ], 36 | "psr-4": { 37 | "Magestat\\SplitOrder\\": "" 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /etc/acl.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /etc/adminhtml/system.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | magestat 9 | 10 | Magestat_SplitOrder::config 11 | 12 | 13 | 14 | 15 | Magento\Config\Model\Config\Source\Yesno 16 | 17 | 18 | 19 | 20 | 21 | 22 | Magestat\SplitOrder\Model\Config\Source\Attributes 23 | Select an attribute to be compared, will be split products with different attribute values. 24 | 25 | 26 | 27 | Magestat\SplitOrder\Model\Config\Source\AttributeQty 28 | You can select which type of value should be returned. 29 | 30 | quantity_and_stock_status 31 | 32 | 33 | 34 | 35 | Magento\Config\Model\Config\Source\Yesno 36 | Set product Out of Stock when Backorder is enabled and inventory number is less than zero. 37 | 38 | status 39 | 40 | 41 | 42 | 43 | Magento\Config\Model\Config\Source\Yesno 44 | Split shipping total between orders, otherwise, place shipping total to one order only. 45 | 46 | 47 |
48 |
49 |
50 | -------------------------------------------------------------------------------- /etc/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | Magento\Checkout\Model\Session\Proxy 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /etc/module.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /registration.php: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | 16 | Magestat_SplitOrder::success.phtml 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /view/frontend/templates/success.phtml: -------------------------------------------------------------------------------- 1 | getOrderArray(); 7 | 8 | // @codingStandardsIgnoreFile 9 | ?> 10 |
11 | getOrderId()):?> 12 | 13 |

14 |
    15 | $incremental): ?> 16 | getCanViewOrder()) :?> 17 |
  • %s', $this->getUrl('sales/order/view/', ['order_id' => $order]), $block->escapeHtml($incremental)) ?>
  • 18 | 19 |
  • escapeHtml($incremental) ?>
  • 20 | 21 | 22 |
23 | 24 | getCanViewOrder()) :?> 25 |

%s', $block->escapeHtml($block->getViewOrderUrl()), $block->escapeHtml($block->getOrderId()))) ?>

26 | 27 |

%1.', $block->escapeHtml($block->getOrderId())) ?>

28 | 29 | 30 |

31 | 32 | 33 | getAdditionalInfoHtml() ?> 34 | 35 |
36 |
37 | 38 |
39 |
40 |
41 | --------------------------------------------------------------------------------