admin 门店库存盘点
parent
fbbd46e614
commit
59fd71ae02
|
|
@ -8,7 +8,6 @@ use App\Models\{User, ProductCategory};
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use Dcat\Admin\{Form, Grid, Admin, Show};
|
use Dcat\Admin\{Form, Grid, Admin, Show};
|
||||||
use Dcat\Admin\Http\Controllers\AdminController;
|
use Dcat\Admin\Http\Controllers\AdminController;
|
||||||
use Illuminate\Database\Eloquent\Relations\Relation;
|
|
||||||
|
|
||||||
class StockController extends AdminController
|
class StockController extends AdminController
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Controllers\Store;
|
||||||
|
|
||||||
|
use Dcat\Admin\Http\Controllers\AdminController;
|
||||||
|
use Dcat\Admin\Grid;
|
||||||
|
use App\Models\Store\{Store, StockLog};
|
||||||
|
use App\Models\{ProductCategory};
|
||||||
|
use App\Admin\Repositories\StoreTockRepository;
|
||||||
|
|
||||||
|
class StockTotalController extends AdminController
|
||||||
|
{
|
||||||
|
protected $translation = 'store-stock-total';
|
||||||
|
|
||||||
|
protected function grid()
|
||||||
|
{
|
||||||
|
// return Grid::make(ProductSku::with(['productSku.category', 'stockLogs']), function (Grid $grid) {
|
||||||
|
return Grid::make(new StoreTockRepository, function (Grid $grid) {
|
||||||
|
$tags = StockLog::tags()->get();
|
||||||
|
$grid->column('name', '产品');
|
||||||
|
$grid->column('category_name', '分类');
|
||||||
|
$grid->column('cost_price', '成本单价');
|
||||||
|
$grid->column('sell_price', '售价');
|
||||||
|
|
||||||
|
$grid->column('tag_in', '入库数');
|
||||||
|
$grid->column('tag_sell', '销售数');
|
||||||
|
$grid->column('tag_online', '线上销售数');
|
||||||
|
$grid->column('tag_loss', '报损数');
|
||||||
|
$grid->column('tag_self', '自用数');
|
||||||
|
$grid->column('tag_out', '调货数');
|
||||||
|
|
||||||
|
$grid->column('stock', '现有库存')->sortable();
|
||||||
|
$grid->column('total_profit', '商品总毛利');
|
||||||
|
|
||||||
|
$grid->disableActions();
|
||||||
|
|
||||||
|
$grid->filter(function (Grid\Filter $filter) use ($tags) {
|
||||||
|
$filter->panel();
|
||||||
|
$filter->equal('store_id', '门店')->select(Store::pluck('title', 'id'))->width(3);
|
||||||
|
$filter->equal('category_id', '分类')->select(ProductCategory::selectOptions())->width(3);
|
||||||
|
$filter->equal('product_name', '商品')->width(3);
|
||||||
|
$filter->between('date', '时间')->date()->width(6);
|
||||||
|
});
|
||||||
|
|
||||||
|
$grid->footer(function ($collection) use ($grid) {
|
||||||
|
$total = $grid->model()->repository()->getTotal($grid->model());
|
||||||
|
$html = '<table class="table table-bordered"><tbody><tr><td></td><td></td><td></td>';
|
||||||
|
$html .= '<td>入库总成本: '.data_get($total, 'total_in').'</td>';
|
||||||
|
$html .= '<td>销售总金额: '.data_get($total, 'total_sell').'</td>';
|
||||||
|
$html .= '<td>线上销售总金额: '.data_get($total, 'total_online').'</td>';
|
||||||
|
$html .= '<td>报损总成本: '.data_get($total, 'total_loss').'</td>';
|
||||||
|
$html .= '<td>自用总成本: '.data_get($total, 'total_self').'</td>';
|
||||||
|
$html .= '<td>调货总金额: '.data_get($total, 'total_out').'</td>';
|
||||||
|
$html .= '<td>现有总成本: '.data_get($total, 'total_cost_price').'</td>';
|
||||||
|
$html .= '<td>现有总价值: '.data_get($total, 'total_sell_price').'</td>';
|
||||||
|
$html .= '<td>产品毛利汇总: '.data_get($total, 'total_profit').'</td>';
|
||||||
|
$html .= '</tr></tbody></table>';
|
||||||
|
return $html;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,144 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Admin\Repositories;
|
||||||
|
|
||||||
|
use Dcat\Admin\Grid;
|
||||||
|
use App\Models\Store\{StockLog, ProductSku};
|
||||||
|
use Dcat\Admin\Repositories\Repository;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
|
||||||
|
class StoreTockRepository extends Repository
|
||||||
|
{
|
||||||
|
public function get(Grid\Model $model)
|
||||||
|
{
|
||||||
|
// 当前页数
|
||||||
|
$currentPage = $model->getCurrentPage();
|
||||||
|
// 每页显示行数
|
||||||
|
$perPage = $model->getPerPage();
|
||||||
|
|
||||||
|
$query = $this->getQuery($model);
|
||||||
|
|
||||||
|
$list = $query->paginate($perPage, ['*'], 'page', $currentPage);
|
||||||
|
$total = $query->count();
|
||||||
|
$tags = StockLog::tags()->get();
|
||||||
|
$data = [];
|
||||||
|
foreach($list as $item) {
|
||||||
|
$stock = $item->stockLogs;
|
||||||
|
$in = $stock->where('product_sku_id', $item->product_sku_id)->where('tag_id', StockLog::TAG_IN)->sum('amount');
|
||||||
|
$loss = $stock->where('product_sku_id', $item->product_sku_id)->where('tag_id', StockLog::TAG_LOSS)->sum('amount');
|
||||||
|
$self = $stock->where('product_sku_id', $item->product_sku_id)->where('tag_id', StockLog::TAG_SELF)->sum('amount');
|
||||||
|
$sell_price = data_get($item->productSku, 'sell_price') / 100;
|
||||||
|
$cost_price = data_get($item->productSku, 'cost_price') / 100;
|
||||||
|
$subData = [
|
||||||
|
'id' => $item->product_sku_id,
|
||||||
|
'name' => data_get($item->productSku, 'name'),
|
||||||
|
'category_id' => data_get($item->productSku, 'category.id'),
|
||||||
|
'category_name' => data_get($item->productSku, 'category.name'),
|
||||||
|
'cost_price' => $cost_price,
|
||||||
|
'sell_price' => $sell_price,
|
||||||
|
'stock' => data_get($item, 'amount'),
|
||||||
|
// 入库数
|
||||||
|
'tag_in' => $in,
|
||||||
|
// 销售数 = 提货数 + 发货数
|
||||||
|
'tag_sell' => $stock->where('product_sku_id', $item->product_sku_id)->whereIn('tag_id', [StockLog::TAG_CARRY, StockLog::TAG_SEND])->sum('amount'),
|
||||||
|
// 线上销售数 = 发货数
|
||||||
|
'tag_online' => $stock->where('product_sku_id', $item->product_sku_id)->where('tag_id', StockLog::TAG_SEND)->sum('amount'),
|
||||||
|
// 报损数
|
||||||
|
'tag_loss' => $loss,
|
||||||
|
// 自用数
|
||||||
|
'tag_self' => $self,
|
||||||
|
// 调货数 = 出库数
|
||||||
|
'tag_out' => $stock->where('product_sku_id', $item->product_sku_id)->where('tag_id', StockLog::TAG_OUT)->sum('amount'),
|
||||||
|
// 毛利 = (入库数-报损数-自用数) x (售价-成本)
|
||||||
|
'total_profit' => floor(($in - $loss - $self) * ($sell_price - $cost_price)),
|
||||||
|
];
|
||||||
|
array_push($data, $subData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $model->makePaginator($total, $data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQuery(Grid\Model $model)
|
||||||
|
{
|
||||||
|
$query = ProductSku::with([
|
||||||
|
'productSku.category',
|
||||||
|
'stockLogs' => function ($q) use ($model) {
|
||||||
|
$q->select('id', 'product_sku_id', DB::raw('abs(`amount`) as `amount`'), 'tag_id');
|
||||||
|
if ($start = $model->filter()->input('date.start')) {
|
||||||
|
$start = Carbon::createFromFormat('Y-m-d', $start);
|
||||||
|
$q->where('created_at', '>=', $start);
|
||||||
|
}
|
||||||
|
if ($end = $model->filter()->input('date.end')) {
|
||||||
|
$end = Carbon::createFromFormat('Y-m-d', $end);
|
||||||
|
$q->where('created_at', '<=', $end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 查询条件
|
||||||
|
$filters = $model->filter()->input();
|
||||||
|
foreach($filters as $key => $value) {
|
||||||
|
if ($key == 'category_id') {
|
||||||
|
$query->whereHas('productSku', fn($q1) => $q1->filter(['category' => $value], \App\Endpoint\Api\Filters\ProductSkuFilter::class));
|
||||||
|
} else if ($key == 'store_id') {
|
||||||
|
$query->where('store_id', $value);
|
||||||
|
} else if ($key == 'product_name') {
|
||||||
|
$query->whereHas('productSku', fn($q1) => $q1->filter(['keyword' => $value], \App\Endpoint\Api\Filters\ProductSkuFilter::class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 排序条件
|
||||||
|
[$orderColumn, $orderType] = $model->getSort();
|
||||||
|
if ($orderColumn == 'stock') {
|
||||||
|
$query->orderBy('amount', $orderType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTotal(Grid\Model $model)
|
||||||
|
{
|
||||||
|
$query = $this->getQuery($model);
|
||||||
|
$list = $query->get();
|
||||||
|
$data = [
|
||||||
|
// 入库总成本 = 总数量 * 成本价
|
||||||
|
'total_in' => 0,
|
||||||
|
// 销售数 = (提货数 + 发货数) * 售价
|
||||||
|
'total_sell' => 0,
|
||||||
|
// 线上销售总金额 = 发货数 * 售价
|
||||||
|
'total_online' => 0,
|
||||||
|
// 报损总成本 = 报损数 * 成本价
|
||||||
|
'total_loss' => 0,
|
||||||
|
// 自用总成本 = 自用数 * 成本价
|
||||||
|
'total_self' => 0,
|
||||||
|
// 调货总成本 = 调货数(出库) * 成本价
|
||||||
|
'total_out' => 0,
|
||||||
|
// 现有总成本 = 库存 * 成本价
|
||||||
|
'total_cost_price' => 0,
|
||||||
|
// 现有总价值 = 库存 * 售价
|
||||||
|
'total_sell_price' => 0,
|
||||||
|
// 毛利 = (入库数-报损数-自用数) x (售价-成本)
|
||||||
|
'total_profit' => 0,
|
||||||
|
];
|
||||||
|
foreach ($list as $item) {
|
||||||
|
$stock = $item->stockLogs;
|
||||||
|
$cost_price = data_get($item, 'productSku.cost_price', 0) / 100;
|
||||||
|
$sell_price = data_get($item, 'productSku.sell_price', 0) / 100;
|
||||||
|
$current_stock = $item->amount;
|
||||||
|
$in_amount = $stock->where('product_sku_id', $item->product_sku_id)->where('tag_id', StockLog::TAG_IN)->sum('amount');
|
||||||
|
$loss_amount = $stock->where('product_sku_id', $item->product_sku_id)->where('tag_id', StockLog::TAG_LOSS)->sum('amount');
|
||||||
|
$self_amount = $stock->where('product_sku_id', $item->product_sku_id)->where('tag_id', StockLog::TAG_SELF)->sum('amount');
|
||||||
|
|
||||||
|
$data['total_in'] += $in_amount * $cost_price;
|
||||||
|
$data['total_sell'] += $stock->where('product_sku_id', $item->product_sku_id)->whereIn('tag_id', [StockLog::TAG_CARRY, StockLog::TAG_SEND])->sum('amount') * $sell_price;
|
||||||
|
$data['total_online'] += $stock->where('product_sku_id', $item->product_sku_id)->where('tag_id', StockLog::TAG_SEND)->sum('amount') * $sell_price;
|
||||||
|
$data['total_loss'] += $loss_amount * $cost_price;
|
||||||
|
$data['total_self'] += $self_amount * $cost_price;
|
||||||
|
$data['total_out'] += $stock->where('product_sku_id', $item->product_sku_id)->where('tag_id', StockLog::TAG_OUT)->sum('amount') * $cost_price;
|
||||||
|
$data['total_cost_price'] += $current_stock * $cost_price;
|
||||||
|
$data['total_sell_price'] += $current_stock * $sell_price;
|
||||||
|
$data['total_profit'] += ($in_amount - $loss_amount - $self_amount) * ($sell_price - $cost_price);
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -196,6 +196,7 @@ Route::group([
|
||||||
$router->resource('store/batch', 'Store\StockBatchController')->names('store.batch');
|
$router->resource('store/batch', 'Store\StockBatchController')->names('store.batch');
|
||||||
$router->resource('store/device', 'Store\DeviceController')->names('store.device');
|
$router->resource('store/device', 'Store\DeviceController')->names('store.device');
|
||||||
$router->resource('store/desk', 'Store\DeskController')->names('store.desk');
|
$router->resource('store/desk', 'Store\DeskController')->names('store.desk');
|
||||||
|
$router->get('store/stock-total', 'Store\StockTotalController@index')->name('store.stock_total.index');
|
||||||
|
|
||||||
$router->resource('profit', 'OrderProfitController');
|
$router->resource('profit', 'OrderProfitController');
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,4 +31,9 @@ class ProductSku extends Model
|
||||||
{
|
{
|
||||||
return $this->belongsTo(\App\Models\ProductSpu::class, 'product_spu_id');
|
return $this->belongsTo(\App\Models\ProductSpu::class, 'product_spu_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function stockLogs()
|
||||||
|
{
|
||||||
|
return $this->hasMany(StockLog::class, 'product_sku_id', 'product_sku_id');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,14 @@ class StockLog extends Model
|
||||||
{
|
{
|
||||||
use HasFactory, HasDateTimeFormatter;
|
use HasFactory, HasDateTimeFormatter;
|
||||||
|
|
||||||
|
const TAG_OUT = 1;// 出库, 调出
|
||||||
|
const TAG_IN = 2;// 入库, 进货
|
||||||
|
const TAG_CARRY = 3;// 提货, 线下订单
|
||||||
|
const TAG_LOSS = 4;// 报损
|
||||||
|
const TAG_SELF = 5;// 自用
|
||||||
|
const TAG_SEND = 9;// 发货, 线上订单发货(绑定门店)
|
||||||
|
const TAG_JOIN = 13;// 调入
|
||||||
|
|
||||||
protected $table = 'store_stock_logs';
|
protected $table = 'store_stock_logs';
|
||||||
|
|
||||||
protected $fillable = ['operator_type', 'operator_id', 'operator_name', 'amount', 'product_sku_id', 'remarks', 'balance', 'source_id', 'source_type', 'store_id', 'tag_id'];
|
protected $fillable = ['operator_type', 'operator_id', 'operator_name', 'amount', 'product_sku_id', 'remarks', 'balance', 'source_id', 'source_type', 'store_id', 'tag_id'];
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ trait SkuInfo
|
||||||
return [
|
return [
|
||||||
'spu_id' => $spu->id,
|
'spu_id' => $spu->id,
|
||||||
// 'name' => $spu->name,
|
// 'name' => $spu->name,
|
||||||
'name' => static::createName($spu->name, $skuSpec['specs']),
|
'name' => static::createName('', $skuSpec['specs']),
|
||||||
'subtitle' => $spu->subtitle,
|
'subtitle' => $spu->subtitle,
|
||||||
'category_id' => $spu->category_id,
|
'category_id' => $spu->category_id,
|
||||||
'cover' => $spu->cover,
|
'cover' => $spu->cover,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
return [
|
||||||
|
'labels' => [
|
||||||
|
'store' => '门店管理',
|
||||||
|
'StockTotal' => '库存盘点',
|
||||||
|
'stock-total' => '库存盘点',
|
||||||
|
]
|
||||||
|
];
|
||||||
Loading…
Reference in New Issue