Merge branch 'master' of https://github.com/prashant-webkul/bagisto into development

This commit is contained in:
Prashant Singh 2018-12-27 16:29:57 +05:30
commit 0d64d96c9c
61 changed files with 721 additions and 323 deletions

20
LICENSE Normal file
View File

@ -0,0 +1,20 @@
MIT License
Copyright 2010-2018, 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.

View File

@ -1,5 +1,12 @@
                       ![Bagisto Logo](https://bagisto.com/wp-content/themes/bagisto/images/logo.png)
<p align="center">
<a href="http://www.bagisto.com"><img src="https://bagisto.com/wp-content/themes/bagisto/images/logo.png" alt="Total Downloads"></a>
</p>
<p align="center">
<a href="https://packagist.org/packages/bagisto/bagisto"><img src="https://poser.pugx.org/bagisto/bagisto/d/total.svg" alt="Total Downloads"></a>
<a href="https://packagist.org/packages/bagisto/bagisto"><img src="https://poser.pugx.org/bagisto/bagisto/v/stable.svg" alt="Latest Stable Version"></a>
<a href="https://packagist.org/packages/bagisto/bagisto"><img src="https://poser.pugx.org/bagisto/bagisto/license.svg" alt="License"></a>
</p>
# Topics
1. ### Introduction
@ -110,7 +117,7 @@ composer dump-autoload
### 5. License
Bagisto is a truly opensource E-Commerce framework which will always be free under the [MIT License](https://opensource.org/licenses/MIT).
Bagisto is a truly opensource E-Commerce framework which will always be free under the [MIT License](https://github.com/bagisto/bagisto/blob/master/LICENSE).
### Coming Soon:
-> API support for core packages and numerous fixes.

View File

@ -1,6 +1,6 @@
{
"name": "bagisto/bagisto",
"description": "Bagisto Ecommerce",
"description": "Bagisto Laravel ECommerce",
"keywords": [
"framework",
"laravel"

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
{
"/js/admin.js": "/js/admin.js?id=c70dacdf945a32e04b77",
"/css/admin.css": "/css/admin.css?id=ccb086d72702e662082e"
"/css/admin.css": "/css/admin.css?id=d40a640933cbcc121f1d"
}

View File

@ -53,6 +53,12 @@ class OrderShipmentsDataGrid
'primaryKey' => 'ship.order_id',
'condition' => '=',
'secondaryKey' => 'ors.id',
], [
'join' => 'leftjoin',
'table' => 'inventory_sources as is',
'primaryKey' => 'ship.inventory_source_id',
'condition' => '=',
'secondaryKey' => 'is.id',
]
],
@ -82,6 +88,12 @@ class OrderShipmentsDataGrid
'type' => 'string',
'label' => 'Customer Name',
'sortable' => false,
], [
'name' => 'is.name',
'alias' => 'inventorySourceName',
'type' => 'string',
'label' => 'Inventory Source',
'sortable' => true
], [
'name' => 'ors.created_at',
'alias' => 'orscreated',

View File

@ -76,7 +76,9 @@ class OrderController extends Controller
*/
public function cancel($id)
{
if($this->order->cancel($id)) {
$result = $this->order->cancel($id);
if($result) {
session()->flash('success', trans('Order canceled successfully.'));
} else {
session()->flash('error', trans('Order can not be canceled.'));

View File

@ -5,8 +5,9 @@ namespace Webkul\Admin\Http\Controllers\Sales;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Webkul\Admin\Http\Controllers\Controller;
use Webkul\Sales\Repositories\OrderRepository as Order;
use Webkul\Sales\Repositories\ShipmentRepository as Shipment;
use Webkul\Sales\Repositories\OrderRepository as Order;
use Webkul\Sales\Repositories\OrderItemRepository as OrderItem;
/**
* Sales Shipment controller
@ -23,28 +24,40 @@ class ShipmentController extends Controller
*/
protected $_config;
/**
* OrderRepository object
*
* @var array
*/
protected $order;
/**
* ShipmentRepository object
*
* @var array
* @var mixed
*/
protected $shipment;
/**
* OrderRepository object
*
* @var mixed
*/
protected $order;
/**
* OrderItemRepository object
*
* @var mixed
*/
protected $orderItem;
/**
* Create a new controller instance.
*
* @param Webkul\Sales\Repositories\OrderRepository $order
* @param Webkul\Sales\Repositories\ShipmentRepository $shipment
* @param Webkul\Sales\Repositories\OrderRepository $order
* @param Webkul\Sales\Repositories\OrderitemRepository $orderItem
* @return void
*/
public function __construct(Shipment $shipment, Order $order)
public function __construct(
Shipment $shipment,
Order $order,
OrderItem $orderItem
)
{
$this->middleware('admin');
@ -52,6 +65,8 @@ class ShipmentController extends Controller
$this->order = $order;
$this->orderItem = $orderItem;
$this->shipment = $shipment;
}
@ -76,6 +91,12 @@ class ShipmentController extends Controller
{
$order = $this->order->find($orderId);
if(!$order->channel || !$order->canShip()) {
session()->flash('error', 'Shipment can not be created for this order.');
return redirect()->back();
}
return view($this->_config['view'], compact('order'));
}
@ -99,21 +120,14 @@ class ShipmentController extends Controller
$this->validate(request(), [
'shipment.carrier_title' => 'required',
'shipment.track_number' => 'required',
'shipment.items.*' => 'required|numeric|min:0',
'shipment.source' => 'required',
'shipment.items.*.*' => 'required|numeric|min:0',
]);
$data = request()->all();
$haveProductToShip = false;
foreach ($data['shipment']['items'] as $itemId => $qty) {
if($qty) {
$haveProductToShip = true;
break;
}
}
if(!$haveProductToShip) {
session()->flash('error', 'Shipment can not be created without products.');
if(!$this->isInventoryValidate($data)) {
session()->flash('error', 'Requested quantity is invalid or not available.');
return redirect()->back();
}
@ -125,6 +139,41 @@ class ShipmentController extends Controller
return redirect()->route($this->_config['redirect'], $orderId);
}
/**
* Checks if requested quantity available or not
*
* @param array $data
* @return boolean
*/
public function isInventoryValidate(&$data)
{
$valid = false;
foreach ($data['shipment']['items'] as $itemId => $inventorySource) {
if ($qty = $inventorySource[$data['shipment']['source']]) {
$orderItem = $this->orderItem->find($itemId);
$product = ($orderItem->type == 'configurable')
? $orderItem->child->product
: $orderItem->product;
$inventory = $product->inventories()
->where('inventory_source_id', $data['shipment']['source'])
->first();
if ($orderItem->qty_to_ship < $qty || $inventory->qty < $qty) {
return false;
}
$valid = true;
} else {
unset($data['shipment']['items'][$itemId]);
}
}
return $valid;
}
/**
* Show the view for the specified resource.
*

View File

@ -64,10 +64,5 @@ class Order {
*/
public function updateProductInventory($order)
{
$productListener = app(\Webkul\Admin\Listeners\Product::class);
foreach ($order->items as $item) {
$productListener->afterOrderRecieved($item->product->id, $item->qty_ordered);
}
}
}

View File

@ -218,20 +218,6 @@ class Product {
return true;
}
/**
* Updates the product quantity when the order is received
*
* @param $productId
* @param $itemQuantity
*/
public function afterOrderRecieved($productId, $itemQuantity) {
$productGrid = $this->productGrid->findOneByField('product_id', $productId);
$productGrid->update(['quantity' => $productGrid->quantity - $itemQuantity]);
return true;
}
/**
* Manually invoke this function when you have created the products by importing or seeding or factory.
*/

View File

@ -481,6 +481,10 @@ body {
margin-bottom: 0;
}
}
.radio {
margin: 0;
}
}
.sale-summary {

View File

@ -205,9 +205,14 @@ return [
'save-btn-title' => 'Save Shipment',
'qty-ordered' => 'Qty Ordered',
'qty-to-ship' => 'Qty to Ship',
'available-sources' => 'Available Sources',
'source' => 'Source',
'select-source' => 'Please Select Source',
'qty-available' => 'Qty Available',
'inventory-source' => 'Inventory Source',
'carrier-title' => 'Carrier Title',
'tracking-number' => 'Tracking Number',
'view-title' => 'Shipment #:shipment_id',
'view-title' => 'Shipment #:shipment_id'
]
],
@ -462,6 +467,7 @@ return [
'default-locale' => 'Default Locale',
'currencies' => 'Currencies',
'base-currency' => 'Base Currency',
'inventory_sources' => 'Inventory Sources',
'design' => 'Design',
'theme' => 'Theme',
'home_page_content' => 'Home Page Content',

View File

@ -153,7 +153,7 @@
</span>
<span class="value">
{{ core()->getConfigData('paymentmethods.' . $order->payment->method . '.title') }}
{{ core()->getConfigData('sales.paymentmethods.' . $order->payment->method . '.title') }}
</span>
</div>

View File

@ -142,7 +142,7 @@
<tbody>
<tr>
<td>
{{ core()->getConfigData('paymentmethods.' . $invoice->order->payment->method . '.title') }}
{{ core()->getConfigData('sales.paymentmethods.' . $invoice->order->payment->method . '.title') }}
</td>
<td>
{{ $invoice->order->shipping_title }}

View File

@ -153,7 +153,7 @@
</span>
<span class="value">
{{ core()->getConfigData('paymentmethods.' . $order->payment->method . '.title') }}
{{ core()->getConfigData('sales.paymentmethods.' . $order->payment->method . '.title') }}
</span>
</div>

View File

@ -27,7 +27,7 @@
</a>
@endif
@if($order->canShip())
@if($order->canShip() && $order->channel)
<a href="{{ route('admin.sales.shipments.create', $order->id) }}" class="btn btn-lg btn-primary">
{{ __('admin::app.sales.orders.shipment-btn-title') }}
</a>
@ -160,7 +160,7 @@
</span>
<span class="value">
{{ core()->getConfigData('paymentmethods.' . $order->payment->method . '.title') }}
{{ core()->getConfigData('sales.paymentmethods.' . $order->payment->method . '.title') }}
</span>
</div>
@ -368,6 +368,7 @@
<th>{{ __('admin::app.sales.shipments.order-id') }}</th>
<th>{{ __('admin::app.sales.shipments.order-date') }}</th>
<th>{{ __('admin::app.sales.shipments.customer-name') }}</th>
<th>{{ __('admin::app.sales.shipments.inventory-source') }}</th>
<th>{{ __('admin::app.sales.shipments.total-qty') }}</th>
<th>{{ __('admin::app.sales.shipments.action') }}</th>
</tr>
@ -382,6 +383,9 @@
<td>#{{ $shipment->order->id }}</td>
<td>{{ $shipment->order->created_at }}</td>
<td>{{ $shipment->address->name }}</td>
@if ($shipment->inventory_source)
<td>{{ $shipment->inventory_source->name }}</td>
@endif
<td>{{ $shipment->total_qty }}</td>
<td class="action">
<a href="{{ route('admin.sales.shipments.view', $shipment->id) }}">

View File

@ -153,7 +153,7 @@
</span>
<span class="value">
{{ core()->getConfigData('paymentmethods.' . $order->payment->method . '.title') }}
{{ core()->getConfigData('sales.paymentmethods.' . $order->payment->method . '.title') }}
</span>
</div>
@ -218,49 +218,7 @@
<accordian :title="'{{ __('admin::app.sales.orders.products-ordered') }}'" :active="true">
<div slot="body">
<div class="table">
<table>
<thead>
<tr>
<th>{{ __('admin::app.sales.orders.SKU') }}</th>
<th>{{ __('admin::app.sales.orders.product-name') }}</th>
<th>{{ __('admin::app.sales.shipments.qty-ordered') }}</th>
<th>{{ __('admin::app.sales.shipments.qty-to-ship') }}</th>
</tr>
</thead>
<tbody>
@foreach ($order->items as $item)
@if ($item->qty_to_ship > 0)
<tr>
<td>{{ $item->type == 'configurable' ? $item->child->sku : $item->sku }}</td>
<td>
{{ $item->name }}
@if ($html = $item->getOptionDetailHtml())
<p>{{ $html }}</p>
@endif
</td>
<td>{{ $item->qty_ordered }}</td>
<td>
<div class="control-group" :class="[errors.has('shipment[items][{{ $item->id }}]') ? 'has-error' : '']">
<input type="text" v-validate="'required|numeric|min:0'" class="control" id="shipment[items][{{ $item->id }}]" name="shipment[items][{{ $item->id }}]" value="{{ $item->qty_to_ship }}" data-vv-as="&quot;{{ __('admin::app.sales.shipments.qty-to-ship') }}&quot;"/>
<span class="control-error" v-if="errors.has('shipment[items][{{ $item->id }}]')">
@verbatim
{{ errors.first('shipment[items][<?php echo $item->id ?>]') }}
@endverbatim
</span>
</div>
</td>
</tr>
@endif
@endforeach
</tbody>
</table>
</div>
<order-item-list></order-item-list>
</div>
</accordian>
@ -269,4 +227,129 @@
</div>
</form>
</div>
@stop
@stop
@push('scripts')
<script type="text/x-template" id="order-item-list-template">
<div>
<div class="control-group" :class="[errors.has('shipment[source]') ? 'has-error' : '']">
<label for="shipment[source]" class="required">{{ __('admin::app.sales.shipments.source') }}</label>
<select v-validate="'required'" class="control" name="shipment[source]" id="shipment[source]" data-vv-as="&quot;{{ __('admin::app.sales.shipments.source') }}&quot;" v-model="source">
<option value="">{{ __('admin::app.sales.shipments.select-source') }}</option>
@foreach ($order->channel->inventory_sources as $key => $inventorySource)
<option value="{{ $inventorySource->id }}">{{ $inventorySource->name }}</option>
@endforeach
</select>
<span class="control-error" v-if="errors.has('shipment[source]')">
@{{ errors.first('shipment[source]') }}
</span>
</div>
<div class="table">
<table>
<thead>
<tr>
<th>{{ __('admin::app.sales.orders.SKU') }}</th>
<th>{{ __('admin::app.sales.orders.product-name') }}</th>
<th>{{ __('admin::app.sales.shipments.qty-ordered') }}</th>
<th>{{ __('admin::app.sales.shipments.qty-to-ship') }}</th>
<th>{{ __('admin::app.sales.shipments.available-sources') }}</th>
</tr>
</thead>
<tbody>
@foreach ($order->items as $item)
@if ($item->qty_to_ship > 0 && $item->product)
<tr>
<td>{{ $item->type == 'configurable' ? $item->child->sku : $item->sku }}</td>
<td>
{{ $item->name }}
@if ($html = $item->getOptionDetailHtml())
<p>{{ $html }}</p>
@endif
</td>
<td>{{ $item->qty_ordered }}</td>
<td>{{ $item->qty_to_ship }}</td>
<td>
<table>
<thead>
<tr>
<th>{{ __('admin::app.sales.shipments.source') }}</th>
<th>{{ __('admin::app.sales.shipments.qty-available') }}</th>
<th>{{ __('admin::app.sales.shipments.qty-to-ship') }}</th>
</tr>
</thead>
<tbody>
@foreach ($order->channel->inventory_sources as $key => $inventorySource)
<tr>
<td>
{{ $inventorySource->name }}
</td>
<td>
<?php
if($item->type == 'configurable') {
$sourceQty = $item->child->product->inventory_source_qty($inventorySource);
} else {
$sourceQty = $item->product->inventory_source_qty($inventorySource);
}
?>
{{ $sourceQty }}
</td>
<td>
<?php $inputName = "shipment[items][$item->id][$inventorySource->id]"; ?>
<div class="control-group" :class="[errors.has('{{ $inputName }}') ? 'has-error' : '']">
<input type="text" v-validate="'required|numeric|min_value:0|max_value:{{$sourceQty}}'" class="control" id="{{ $inputName }}" name="{{ $inputName }}" value="0" data-vv-as="&quot;{{ __('admin::app.sales.shipments.qty-to-ship') }}&quot;" :disabled="source != '{{ $inventorySource->id }}'"/>
<span class="control-error" v-if="errors.has('{{ $inputName }}')">
@verbatim
{{ errors.first('<?php echo $inputName; ?>') }}
@endverbatim
</span>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</td>
</tr>
@endif
@endforeach
</tbody>
</table>
</div>
</div>
</script>
<script>
Vue.component('order-item-list', {
template: '#order-item-list-template',
inject: ['$validator'],
data: () => ({
source: ""
})
});
</script>
@endpush

View File

@ -149,7 +149,7 @@
</span>
<span class="value">
{{ core()->getConfigData('paymentmethods.' . $order->payment->method . '.title') }}
{{ core()->getConfigData('sales.paymentmethods.' . $order->payment->method . '.title') }}
</span>
</div>
@ -191,6 +191,18 @@
</span>
</div>
@if ($shipment->inventory_source)
<div class="row">
<span class="title">
{{ __('admin::app.sales.shipments.inventory-source') }}
</span>
<span class="value">
{{ $shipment->inventory_source->name }}
</span>
</div>
@endif
<div class="row">
<span class="title">
{{ __('admin::app.sales.shipments.carrier-title') }}

View File

@ -44,6 +44,18 @@
<textarea class="control" id="description" name="description">{{ old('description') }}</textarea>
</div>
<div class="control-group" :class="[errors.has('inventory_sources[]') ? 'has-error' : '']">
<label for="inventory_sources" class="required">{{ __('admin::app.settings.channels.inventory_sources') }}</label>
<select v-validate="'required'" class="control" id="inventory_sources" name="inventory_sources[]" data-vv-as="&quot;{{ __('admin::app.settings.channels.inventory_sources') }}&quot;" multiple>
@foreach(app('Webkul\Inventory\Repositories\InventorySourceRepository')->all() as $inventorySource)
<option value="{{ $inventorySource->id }}" {{ old('inventory_sources') && in_array($inventorySource->id, old('inventory_sources')) ? 'selected' : '' }}>
{{ $inventorySource->name }}
</option>
@endforeach
</select>
<span class="control-error" v-if="errors.has('inventory_sources[]')">@{{ errors.first('inventory_sources[]') }}</span>
</div>
<div class="control-group">
<label for="hostname">{{ __('admin::app.settings.channels.hostname') }}</label>
<input class="control" id="hostname" name="hostname" value="{{ old('hostname') }}" placeholder="https://www.example.com"/>

View File

@ -46,6 +46,19 @@
<textarea class="control" id="description" name="description">{{ old('description') ?: $channel->description }}</textarea>
</div>
<div class="control-group" :class="[errors.has('inventory_sources[]') ? 'has-error' : '']">
<label for="inventory_sources" class="required">{{ __('admin::app.settings.channels.inventory_sources') }}</label>
<?php $selectedOptionIds = old('inventory_sources') ?: $channel->inventory_sources->pluck('id')->toArray() ?>
<select v-validate="'required'" class="control" id="inventory_sources" name="inventory_sources[]" data-vv-as="&quot;{{ __('admin::app.settings.channels.inventory_sources') }}&quot;" multiple>
@foreach(app('Webkul\Inventory\Repositories\InventorySourceRepository')->all() as $inventorySource)
<option value="{{ $inventorySource->id }}" {{ in_array($inventorySource->id, $selectedOptionIds) ? 'selected' : '' }}>
{{ $inventorySource->name }}
</option>
@endforeach
</select>
<span class="control-error" v-if="errors.has('inventory_sources[]')">@{{ errors.first('inventory_sources[]') }}</span>
</div>
<div class="control-group">
<label for="hostname">{{ __('admin::app.settings.channels.hostname') }}</label>
<input type="text" class="control" id="hostname" name="hostname" value="{{ $channel->hostname }}" placeholder="https://www.example.com"/>

View File

@ -1,8 +1,8 @@
const { mix } = require("laravel-mix");
require("laravel-mix-merge-manifest");
// var publicPath = 'publishable/assets';
var publicPath = "../../../public/vendor/webkul/admin/assets";
var publicPath = 'publishable/assets';
// var publicPath = "../../../public/vendor/webkul/admin/assets";
mix.setPublicPath(publicPath).mergeManifest();
mix.disableNotifications();

View File

@ -537,8 +537,7 @@ class Core
'code' => $field,
'locale_code' => $locale
]);
}
else {
} else {
$coreConfigValue = $this->coreConfigRepository->findOneWhere([
'code' => $field
]);
@ -585,8 +584,8 @@ class Core
{
$collection = [];
foreach ($this->countries() as $country) {
$collection[$country->code] = $this->states($country->code)->toArray();
foreach ($this->countryStateRepository->all() as $state) {
$collection[$state->country_code][] = $state->toArray();
}
return $collection;

View File

@ -0,0 +1,35 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateChannelInventorySourcesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('channel_inventory_sources', function (Blueprint $table) {
$table->integer('channel_id')->unsigned();
$table->integer('inventory_source_id')->unsigned();
$table->unique(['channel_id', 'inventory_source_id']);
$table->foreign('channel_id')->references('id')->on('channels')->onDelete('cascade');
$table->foreign('inventory_source_id')->references('id')->on('inventory_sources')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('channel_inventory_sources');
}
}

View File

@ -120,6 +120,7 @@ class ChannelController extends Controller
'code' => ['required', 'unique:channels,code,' . $id, new \Webkul\Core\Contracts\Validations\Code],
'name' => 'required',
'locales' => 'required|array|min:1',
'inventory_sources' => 'required|array|min:1',
'default_locale_id' => 'required',
'currencies' => 'required|array|min:1',
'base_currency_id' => 'required',

View File

@ -3,9 +3,10 @@
namespace Webkul\Core\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
use Webkul\Core\Models\Locale;
use Webkul\Core\Models\Currency;
use Illuminate\Support\Facades\Storage;
use Webkul\Inventory\Models\InventorySource;
class Channel extends Model
{
@ -35,6 +36,14 @@ class Channel extends Model
return $this->belongsToMany(Currency::class, 'channel_currencies');
}
/**
* Get the channel inventory sources.
*/
public function inventory_sources()
{
return $this->belongsToMany(InventorySource::class, 'channel_inventory_sources');
}
protected $with = ['base_currency'];

View File

@ -35,6 +35,8 @@ class ChannelRepository extends Repository
$channel->currencies()->sync($data['currencies']);
$channel->inventory_sources()->sync($data['inventory_sources']);
$this->uploadImages($data, $channel);
$this->uploadImages($data, $channel, 'favicon');
@ -58,6 +60,8 @@ class ChannelRepository extends Repository
$channel->currencies()->sync($data['currencies']);
$channel->inventory_sources()->sync($data['inventory_sources']);
$this->uploadImages($data, $channel);
$this->uploadImages($data, $channel, 'favicon');

View File

@ -68,7 +68,7 @@ abstract class Payment
*/
public function getConfigData($field)
{
return core()->getConfigData('paymentmethods.' . $this->getCode() . '.' . $field);
return core()->getConfigData('sales.paymentmethods.' . $this->getCode() . '.' . $field);
}
abstract public function getRedirectUrl();

View File

@ -0,0 +1,37 @@
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateProductOrderedInventoriesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('product_ordered_inventories', function (Blueprint $table) {
$table->increments('id');
$table->integer('qty')->default(0);
$table->integer('product_id')->unsigned();
$table->integer('channel_id')->unsigned();
$table->unique(['product_id', 'channel_id']);
$table->foreign('product_id')->references('id')->on('products')->onDelete('cascade');
$table->foreign('channel_id')->references('id')->on('channels')->onDelete('cascade');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('product_ordered_inventories');
}
}

View File

@ -76,6 +76,14 @@ class Product extends Model
return $this->hasMany(ProductInventory::class, 'product_id');
}
/**
* The ordered inventories that belong to the product.
*/
public function ordered_inventories()
{
return $this->hasMany(ProductOrderedInventory::class, 'product_id');
}
/**
* The inventory sources that belong to the product.
*/
@ -140,6 +148,18 @@ class Product extends Model
return false;
}
/**
* @param integer $qty
*
* @return bool
*/
public function inventory_source_qty($inventorySource)
{
return $this->inventories()
->where('inventory_source_id', $inventorySource->id)
->sum('qty');
}
/**
* @param integer $qty
*
@ -147,15 +167,25 @@ class Product extends Model
*/
public function haveSufficientQuantity($qty)
{
$inventories = $this->inventory_sources()->orderBy('priority', 'asc')->get();
$total = 0;
foreach($inventories as $inventorySource) {
if(!$inventorySource->status)
continue;
$channelInventorySourceIds = core()->getCurrentChannel()
->inventory_sources()
->where('status', 1)
->pluck('id');
$total += $inventorySource->pivot->qty;
foreach ($this->inventories as $inventory) {
if(is_numeric($index = $channelInventorySourceIds->search($inventory->inventory_source_id))) {
$total += $inventory->qty;
}
}
$orderedInventory = $this->ordered_inventories()
->where('channel_id', core()->getCurrentChannel()->id)
->first();
if ($orderedInventory) {
$total -= $orderedInventory->qty;
}
return $qty <= $total ? true : false;

View File

@ -0,0 +1,30 @@
<?php
namespace Webkul\Product\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Inventory\Models\InventorySource;
use Webkul\Core\Models\Channel;
class ProductOrderedInventory extends Model
{
public $timestamps = false;
protected $fillable = ['qty', 'product_id', 'channel_id'];
/**
* Get the channel owns the inventory.
*/
public function channel()
{
return $this->belongsTo(Channel::class);
}
/**
* Get the product that owns the product inventory.
*/
public function product()
{
return $this->belongsTo(Product::class);
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace Webkul\Product\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Inventory\Models\InventorySource;
use Webkul\Core\Models\Channel;
class ProductSalableInventory extends Model
{
public $timestamps = false;
protected $fillable = ['qty', 'sold_qty', 'product_id', 'channel_id'];
/**
* Get the channel owns the inventory.
*/
public function channel()
{
return $this->belongsTo(Channel::class);
}
// /**
// * Get the inventory source owns the product.
// */
// public function inventory_source()
// {
// return $this->belongsTo(InventorySource::class);
// }
/**
* Get the product that owns the product inventory.
*/
public function product()
{
return $this->belongsTo(Product::class);
}
}

View File

@ -24,12 +24,15 @@ class ProductInventoryRepository extends Repository
}
/**
* @param array $inventories
* @param array $data
* @param mixed $product
* @return mixed
*/
public function saveInventories(array $data, $product)
{
if ($product->type == 'configurable')
return;
$inventorySourceIds = $product->inventory_sources->pluck('id');
if(isset($data['inventories'])) {

View File

@ -1,7 +0,0 @@
<?php
namespace Webkul\Sales\Contracts;
interface OrderItemInventory
{
}

View File

@ -4,7 +4,7 @@ use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateOrderItemInventories extends Migration
class AlterShipmentsTable extends Migration
{
/**
* Run the migrations.
@ -13,12 +13,9 @@ class CreateOrderItemInventories extends Migration
*/
public function up()
{
Schema::create('order_item_inventories', function (Blueprint $table) {
$table->increments('id');
$table->integer('qty')->default(0);
Schema::table('shipments', function (Blueprint $table) {
$table->integer('inventory_source_id')->unsigned()->nullable();
$table->integer('order_item_id')->unsigned()->nullable();
$table->timestamps();
$table->foreign('inventory_source_id')->references('id')->on('inventory_sources')->onDelete('set null');
});
}
@ -29,6 +26,6 @@ class CreateOrderItemInventories extends Migration
*/
public function down()
{
Schema::dropIfExists('order_item_inventories');
//
}
}

View File

@ -22,7 +22,8 @@ class Order extends Model implements OrderContract
/**
* Get the order items record associated with the order.
*/
public function getCustomerFullNameAttribute() {
public function getCustomerFullNameAttribute()
{
return $this->customer_first_name . ' ' . $this->customer_last_name;
}
@ -53,21 +54,24 @@ class Order extends Model implements OrderContract
/**
* Get the order items record associated with the order.
*/
public function items() {
public function items()
{
return $this->hasMany(OrderItemProxy::modelClass())->whereNull('parent_id');
}
/**
* Get the order shipments record associated with the order.
*/
public function shipments() {
public function shipments()
{
return $this->hasMany(ShipmentProxy::modelClass());
}
/**
* Get the order invoices record associated with the order.
*/
public function invoices() {
public function invoices()
{
return $this->hasMany(InvoiceProxy::modelClass());
}

View File

@ -17,21 +17,24 @@ class OrderItem extends Model implements OrderItemContract
/**
* Get remaining qty for shipping.
*/
public function getQtyToShipAttribute() {
public function getQtyToShipAttribute()
{
return $this->qty_ordered - $this->qty_shipped - $this->qty_refunded - $this->qty_canceled;
}
/**
* Get remaining qty for invoice.
*/
public function getQtyToInvoiceAttribute() {
public function getQtyToInvoiceAttribute()
{
return $this->qty_ordered - $this->qty_invoiced - $this->qty_canceled;
}
/**
* Get remaining qty for cancel.
*/
public function getQtyToCancelAttribute() {
public function getQtyToCancelAttribute()
{
return $this->qty_ordered - $this->qty_canceled - $this->qty_invoiced;
}
@ -59,24 +62,19 @@ class OrderItem extends Model implements OrderItemContract
return $this->hasOne(OrderItemProxy::modelClass(), 'parent_id');
}
/**
* Get the inventories record associated with the order item.
*/
public function inventories() {
return $this->hasMany(CartItemInventoryProxy::modelClass());
}
/**
* Get the invoice items record associated with the order item.
*/
public function invoice_items() {
public function invoice_items()
{
return $this->hasMany(InvoiceItemProxy::modelClass());
}
/**
* Get the shipment items record associated with the order item.
*/
public function shipment_items() {
public function shipment_items()
{
return $this->hasMany(ShipmentItemProxy::modelClass());
}

View File

@ -1,19 +0,0 @@
<?php
namespace Webkul\Sales\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Sales\Contracts\OrderItemInventory as OrderItemInventoryContract;
class OrderItemInventory extends Model implements OrderItemInventoryContract
{
protected $guarded = ['id', 'child', 'created_at', 'updated_at'];
/**
* Get the order item record associated with the order item inventory.
*/
public function order_item()
{
return $this->belongsTo(OrderItemProxy::modelClass());
}
}

View File

@ -1,10 +0,0 @@
<?php
namespace Webkul\Sales\Models;
use Konekt\Concord\Proxies\ModelProxy;
class OrderItemInventoryProxy extends ModelProxy
{
}

View File

@ -3,6 +3,7 @@
namespace Webkul\Sales\Models;
use Illuminate\Database\Eloquent\Model;
use Webkul\Inventory\Models\InventorySource;
use Webkul\Sales\Contracts\Shipment as ShipmentContract;
class Shipment extends Model implements ShipmentContract
@ -20,10 +21,19 @@ class Shipment extends Model implements ShipmentContract
/**
* Get the shipment items record associated with the shipment.
*/
public function items() {
public function items()
{
return $this->hasMany(ShipmentItemProxy::modelClass());
}
/**
* Get the inventory source associated with the shipment.
*/
public function inventory_source()
{
return $this->belongsTo(InventorySource::class, 'inventory_source_id');
}
/**
* Get the customer record associated with the shipment.
*/

View File

@ -9,7 +9,6 @@ class ModuleServiceProvider extends BaseModuleServiceProvider
protected $models = [
\Webkul\Sales\Models\Order::class,
\Webkul\Sales\Models\OrderItem::class,
\Webkul\Sales\Models\OrderItemInventory::class,
\Webkul\Sales\Models\OrderAddress::class,
\Webkul\Sales\Models\OrderPayment::class,
\Webkul\Sales\Models\Invoice::class,

View File

@ -1,78 +0,0 @@
<?php
namespace Webkul\Sales\Repositories;
use Illuminate\Container\Container as App;
use Webkul\Core\Eloquent\Repository;
/**
* Order Item Inventory Reposotory
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class OrderItemInventoryRepository extends Repository
{
/**
* Specify Model class name
*
* @return Mixed
*/
function model()
{
return 'Webkul\Sales\Contracts\OrderItemInventory';
}
/**
* @param array $data
* @return mixed
*/
public function create(array $data)
{
$orderItem = $data['orderItem'];
$orderedQuantity = $orderItem->qty_ordered;
$product = $orderItem->type == 'configurable' ? $orderItem->child->product : $orderItem->product;
if($product) {
$inventories = $product->inventory_sources()->orderBy('priority', 'asc')->get();
foreach($inventories as $inventorySource) {
if(!$orderedQuantity)
break;
$sourceQuantity = $inventorySource->pivot->qty;
if(!$inventorySource->status || !$sourceQuantity)
continue;
if($sourceQuantity >= $orderedQuantity) {
$orderItemQuantity = $orderedQuantity;
$sourceQuantity -= $orderItemQuantity;
$orderedQuantity = 0;
} else {
$orderItemQuantity = $sourceQuantity;
$sourceQuantity = 0;
$orderedQuantity -= $orderItemQuantity;
}
$this->model->create([
'qty' => $orderItemQuantity,
'order_item_id' => $orderItem->id,
'inventory_source_id' => $inventorySource->id,
]);
$inventorySource->pivot->update([
'qty' => $sourceQuantity
]);
}
}
}
}

View File

@ -19,7 +19,6 @@ class OrderItemRepository extends Repository
*
* @return Mixed
*/
function model()
{
return 'Webkul\Sales\Contracts\OrderItem';
@ -78,4 +77,62 @@ class OrderItemRepository extends Repository
return $orderItem;
}
/**
* @param mixed $orderItem
* @return void
*/
public function manageInventory($orderItem)
{
if(!$orderedQuantity = $orderItem->qty_ordered)
return;
$product = $orderItem->type == 'configurable' ? $orderItem->child->product : $orderItem->product;
if(!$product) {
return;
}
$orderedInventory = $product->ordered_inventories()
->where('channel_id', $orderItem->order->channel->id)
->first();
if($orderedInventory) {
$orderedInventory->update([
'qty' => $orderedInventory->qty + $orderItem->qty_ordered
]);
} else {
$product->ordered_inventories()->create([
'qty' => $orderItem->qty_ordered,
'product_id' => $product->id,
'channel_id' => $orderItem->order->channel->id,
]);
}
}
/**
* Returns qty to product inventory after order cancelation
*
* @param mixed $orderItem
* @return void
*/
public function returnQtyToProductInventory($orderItem)
{
if (!$product = $orderItem->product)
return;
$orderedInventory = $product->ordered_inventories()
->where('channel_id', $orderItem->order->channel->id)
->first();
if ($orderedInventory) {
if (($qty = $orderedInventory->qty - $orderItem->qty_to_cancel) < 0) {
$qty = 0;
}
$orderedInventory->update([
'qty' => $qty
]);
}
}
}

View File

@ -8,7 +8,6 @@ use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\Model;
use Webkul\Core\Eloquent\Repository;
use Webkul\Sales\Repositories\OrderItemRepository;
use Webkul\Sales\Repositories\OrderItemInventoryRepository;
/**
* Order Reposotory
@ -26,30 +25,19 @@ class OrderRepository extends Repository
*/
protected $orderItem;
/**
* OrderItemInventoryRepository object
*
* @var Object
*/
protected $orderItemInventory;
/**
* Create a new repository instance.
*
* @param Webkul\Sales\Repositories\OrderItemRepository $orderItem
* @param Webkul\Sales\Repositories\OrderItemInventoryRepository $orderItemInventory
* @param Webkul\Sales\Repositories\OrderItemRepository $orderItem
* @return void
*/
public function __construct(
OrderItemRepository $orderItem,
OrderItemInventoryRepository $orderItemInventory,
App $app
)
{
$this->orderItem = $orderItem;
$this->orderItemInventory = $orderItemInventory;
parent::__construct($app);
}
@ -90,7 +78,7 @@ class OrderRepository extends Repository
unset($data['channel']);
}
$data['status'] = core()->getConfigData('paymentmethods.' . $data['payment']['method'] . '.order_status') ?? 'pending';
$data['status'] = core()->getConfigData('sales.paymentmethods.' . $data['payment']['method'] . '.order_status') ?? 'pending';
$order = $this->model->create(array_merge($data, ['increment_id' => $this->generateIncrementId()]));
@ -107,7 +95,7 @@ class OrderRepository extends Repository
$orderItem->child = $this->orderItem->create(array_merge($item['child'], ['order_id' => $order->id, 'parent_id' => $orderItem->id]));
}
$this->orderItemInventory->create(['orderItem' => $orderItem]);
$this->orderItem->manageInventory($orderItem);
}
Event::fire('checkout.order.save.after', $order);
@ -135,6 +123,8 @@ class OrderRepository extends Repository
foreach($order->items as $item) {
if($item->qty_to_cancel) {
$this->orderItem->returnQtyToProductInventory($item);
$item->qty_canceled += $item->qty_to_cancel;
$item->save();

View File

@ -0,0 +1,64 @@
<?php
namespace Webkul\Sales\Repositories;
use Illuminate\Container\Container as App;
use Webkul\Core\Eloquent\Repository;
/**
* ShipmentItem Reposotory
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class ShipmentItemRepository extends Repository
{
/**
* Specify Model class name
*
* @return Mixed
*/
function model()
{
return 'Webkul\Sales\Contracts\ShipmentItem';
}
/**
* @param array $data
* @return void
*/
public function updateProductInventory($data)
{
$orderedInventory = $data['product']->ordered_inventories()
->where('channel_id', $data['shipment']->order->channel->id)
->first();
if ($orderedInventory) {
if (($orderedQty = $orderedInventory->qty - $data['qty']) < 0) {
$orderedQty = 0;
}
$orderedInventory->update([
'qty' => $orderedQty
]);
} else {
$data['product']->ordered_inventories()->create([
'qty' => $data['qty'],
'product_id' => $data['product']->id,
'channel_id' => $data['shipment']->order->channel->id
]);
}
$inventory = $data['product']->inventories()
->where('inventory_source_id', $data['shipment']->inventory_source_id)
->first();
if (($qty = $inventory->qty - $data['qty']) < 0) {
$qty = 0;
}
$inventory->update([
'qty' => $qty
]);
}
}

View File

@ -88,31 +88,33 @@ class ShipmentRepository extends Repository
$order = $this->order->find($data['order_id']);
$totalQty = array_sum($data['shipment']['items']);
$shipment = $this->model->create([
'order_id' => $order->id,
'total_qty' => $totalQty,
'total_qty' => 0,
'carrier_title' => $data['shipment']['carrier_title'],
'track_number' => $data['shipment']['track_number'],
'customer_id' => $order->customer_id,
'customer_type' => $order->customer_type,
'order_address_id' => $order->shipping_address->id,
'inventory_source_id' => $data['shipment']['source'],
]);
foreach ($data['shipment']['items'] as $itemId => $qty) {
if(!$qty) continue;
$totalQty = 0;
foreach ($data['shipment']['items'] as $itemId => $inventorySource) {
$qty = $inventorySource[$data['shipment']['source']];
$orderItem = $this->orderItem->find($itemId);
if($qty > $orderItem->qty_to_ship)
$qty = $orderItem->qty_to_ship;
$totalQty += $qty;
$shipmentItem = $this->shipmentItem->create([
'shipment_id' => $shipment->id,
'order_item_id' => $orderItem->id,
'name' => $orderItem->name,
'sku' => ($orderItem->type == 'configurable' ? $orderItem->child->sku : $orderItem->sku),
'sku' => ($orderItem->type == 'configurable')
? $orderItem->child->sku
: $orderItem->sku,
'qty' => $qty,
'weight' => $orderItem->weight * $qty,
'price' => $orderItem->price,
@ -124,9 +126,24 @@ class ShipmentRepository extends Repository
'additional' => $orderItem->additional,
]);
$product = ($orderItem->type == 'configurable')
? $orderItem->child->product
: $orderItem->product;
$this->shipmentItem->updateProductInventory([
'shipment' => $shipment,
'shipmentItem' => $shipmentItem,
'product' => $product,
'qty' => $qty
]);
$this->orderItem->update(['qty_shipped' => $orderItem->qty_shipped + $qty], $orderItem->id);
}
$shipment->update([
'total_qty' => $totalQty
]);
$this->order->updateOrderStatus($order);
Event::fire('sales.shipment.save.after', $shipment);

View File

@ -1,27 +0,0 @@
<?php
namespace Webkul\Sales\Repositories;
use Illuminate\Container\Container as App;
use Webkul\Core\Eloquent\Repository;
/**
* ShipmentItem Reposotory
*
* @author Jitendra Singh <jitendra@webkul.com>
* @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
*/
class ShipmentItemRepository extends Repository
{
/**
* Specify Model class name
*
* @return Mixed
*/
function model()
{
return 'Webkul\Sales\Contracts\ShipmentItem';
}
}

View File

@ -69,7 +69,7 @@ abstract class AbstractShipping
*/
public function getConfigData($field)
{
return core()->getConfigData('carriers.' . $this->getCode() . '.' . $field);
return core()->getConfigData('sales.carriers.' . $this->getCode() . '.' . $field);
}
}
?>

View File

@ -5,6 +5,7 @@ namespace Webkul\Shipping\Carriers;
use Config;
use Webkul\Checkout\Models\CartShippingRate;
use Webkul\Shipping\Facades\Shipping;
use Webkul\Checkout\Facades\Cart;
/**
* Class Rate.
@ -29,6 +30,8 @@ class FlatRate extends AbstractShipping
if(!$this->isAvailable())
return false;
$cart = Cart::getCart();
$object = new CartShippingRate;
$object->carrier = 'flatrate';
@ -36,8 +39,15 @@ class FlatRate extends AbstractShipping
$object->method = 'flatrate_flatrate';
$object->method_title = $this->getConfigData('title');
$object->method_description = $this->getConfigData('description');
$object->price = core()->convertPrice($this->getConfigData('default_rate'));
$object->base_price = $this->getConfigData('default_rate');
if ($this->getConfigData('type') == 'per_unit') {
$object->price = core()->convertPrice($this->getConfigData('default_rate')) * $cart->items_qty;
$object->base_price = $this->getConfigData('default_rate') * $cart->items_qty;
} else {
$object->price = core()->convertPrice($this->getConfigData('default_rate'));
$object->base_price = $this->getConfigData('default_rate');
}
return $object;
}

View File

@ -74,9 +74,9 @@ return [
'options' => [
[
'title' => 'Per Unit',
'value' => 'Per Order'
'value' => 'per_unit'
], [
'title' => 'Inactive',
'title' => 'Per Order',
'value' => 'per_order'
]
],
@ -99,7 +99,7 @@ return [
]
], [
'key' => 'sales.shipping',
'name' => 'Shipping Origin',
'name' => 'Shipping',
'sort' => 0
], [
'key' => 'sales.shipping.origin',

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
{
"/js/shop.js": "/js/shop.js?id=dc2ea56a854d779e7089",
"/css/shop.css": "/css/shop.css?id=7c64b6577e4bcb5b03ee"
"/css/shop.css": "/css/shop.css?id=7aa91d217344fc8f4f53"
}

View File

@ -122,7 +122,7 @@
</div>
<div class="text">
{{ core()->getConfigData('paymentmethods.' . $cart->payment->method . '.title') }}
{{ core()->getConfigData('sales.paymentmethods.' . $cart->payment->method . '.title') }}
</div>
</div>

View File

@ -142,7 +142,7 @@
<tbody>
<tr>
<td>
{{ core()->getConfigData('paymentmethods.' . $invoice->order->payment->method . '.title') }}
{{ core()->getConfigData('sales.paymentmethods.' . $invoice->order->payment->method . '.title') }}
</td>
<td>
{{ $invoice->order->shipping_title }}

View File

@ -483,7 +483,7 @@
</div>
<div class="box-content">
{{ core()->getConfigData('paymentmethods.' . $order->payment->method . '.title') }}
{{ core()->getConfigData('sales.paymentmethods.' . $order->payment->method . '.title') }}
</div>
</div>
</div>

View File

@ -73,7 +73,9 @@
<span class="control-error" v-if="errors.has('agreement')">@{{ errors.first('agreement') }}</span>
</div> --}}
<input class="btn btn-primary btn-lg" type="submit" value="{{ __('shop::app.customer.signup-form.button_title') }}">
<button class="btn btn-primary btn-lg" type="submit">
{{ __('shop::app.customer.signup-form.button_title') }}
</button>
</div>
</form>

View File

@ -91,7 +91,7 @@
</div>
<div style="font-weight: bold;font-size: 16px; color: #242424;">
{{ core()->getConfigData('paymentmethods.' . $order->payment->method . '.title') }}
{{ core()->getConfigData('sales.paymentmethods.' . $order->payment->method . '.title') }}
</div>
</div>
</div>

View File

@ -89,7 +89,7 @@
</div>
<div style="font-weight: bold;font-size: 16px; color: #242424;">
{{ core()->getConfigData('paymentmethods.' . $order->payment->method . '.title') }}
{{ core()->getConfigData('sales.paymentmethods.' . $order->payment->method . '.title') }}
</div>
</div>
</div>

View File

@ -101,7 +101,7 @@
</div>
<div style="font-weight: bold;font-size: 16px; color: #242424;">
{{ core()->getConfigData('paymentmethods.' . $order->payment->method . '.title') }}
{{ core()->getConfigData('sales.paymentmethods.' . $order->payment->method . '.title') }}
</div>
</div>
</div>

View File

@ -1,8 +1,8 @@
const { mix } = require("laravel-mix");
require("laravel-mix-merge-manifest");
// var publicPath = 'publishable/assets';
var publicPath = "../../../public/themes/default/assets";
var publicPath = 'publishable/assets';
// var publicPath = "../../../public/themes/default/assets";
mix.setPublicPath(publicPath).mergeManifest();
mix.disableNotifications();

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
{
"/js/ui.js": "/js/ui.js?id=c3ee60fd11e29aca2922",
"/css/ui.css": "/css/ui.css?id=8d57ae10a97f50169f1f"
"/css/ui.css": "/css/ui.css?id=0433c5b81c6583e79c04"
}