diff --git a/app/Admin/Controllers/Store/StockController.php b/app/Admin/Controllers/Store/StockController.php index ccef755d..8970ba15 100644 --- a/app/Admin/Controllers/Store/StockController.php +++ b/app/Admin/Controllers/Store/StockController.php @@ -8,7 +8,6 @@ use App\Models\{User, ProductCategory}; use Carbon\Carbon; use Dcat\Admin\{Form, Grid, Admin, Show}; use Dcat\Admin\Http\Controllers\AdminController; -use Illuminate\Database\Eloquent\Relations\Relation; class StockController extends AdminController { diff --git a/app/Admin/Controllers/Store/StockTotalController.php b/app/Admin/Controllers/Store/StockTotalController.php new file mode 100644 index 00000000..513c41d8 --- /dev/null +++ b/app/Admin/Controllers/Store/StockTotalController.php @@ -0,0 +1,62 @@ +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 = ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= ''; + $html .= '
入库总成本: '.data_get($total, 'total_in').'销售总金额: '.data_get($total, 'total_sell').'线上销售总金额: '.data_get($total, 'total_online').'报损总成本: '.data_get($total, 'total_loss').'自用总成本: '.data_get($total, 'total_self').'调货总金额: '.data_get($total, 'total_out').'现有总成本: '.data_get($total, 'total_cost_price').'现有总价值: '.data_get($total, 'total_sell_price').'产品毛利汇总: '.data_get($total, 'total_profit').'
'; + return $html; + }); + }); + } +} diff --git a/app/Admin/Repositories/StoreTockRepository.php b/app/Admin/Repositories/StoreTockRepository.php new file mode 100644 index 00000000..dda8fa2a --- /dev/null +++ b/app/Admin/Repositories/StoreTockRepository.php @@ -0,0 +1,144 @@ +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; + } +} diff --git a/app/Admin/routes.php b/app/Admin/routes.php index 05834f23..cc703fe2 100644 --- a/app/Admin/routes.php +++ b/app/Admin/routes.php @@ -196,6 +196,7 @@ Route::group([ $router->resource('store/batch', 'Store\StockBatchController')->names('store.batch'); $router->resource('store/device', 'Store\DeviceController')->names('store.device'); $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'); diff --git a/app/Models/Store/ProductSku.php b/app/Models/Store/ProductSku.php index 73fa8759..5f01257d 100644 --- a/app/Models/Store/ProductSku.php +++ b/app/Models/Store/ProductSku.php @@ -31,4 +31,9 @@ class ProductSku extends Model { return $this->belongsTo(\App\Models\ProductSpu::class, 'product_spu_id'); } + + public function stockLogs() + { + return $this->hasMany(StockLog::class, 'product_sku_id', 'product_sku_id'); + } } diff --git a/app/Models/Store/StockLog.php b/app/Models/Store/StockLog.php index 51149609..10026467 100644 --- a/app/Models/Store/StockLog.php +++ b/app/Models/Store/StockLog.php @@ -11,6 +11,14 @@ class StockLog extends Model { 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 $fillable = ['operator_type', 'operator_id', 'operator_name', 'amount', 'product_sku_id', 'remarks', 'balance', 'source_id', 'source_type', 'store_id', 'tag_id']; diff --git a/app/Traits/SkuInfo.php b/app/Traits/SkuInfo.php index bb521364..f717d52c 100644 --- a/app/Traits/SkuInfo.php +++ b/app/Traits/SkuInfo.php @@ -104,7 +104,7 @@ trait SkuInfo return [ 'spu_id' => $spu->id, // 'name' => $spu->name, - 'name' => static::createName($spu->name, $skuSpec['specs']), + 'name' => static::createName('', $skuSpec['specs']), 'subtitle' => $spu->subtitle, 'category_id' => $spu->category_id, 'cover' => $spu->cover, diff --git a/resources/lang/zh_CN/store-stock-total.php b/resources/lang/zh_CN/store-stock-total.php new file mode 100644 index 00000000..736d2e4c --- /dev/null +++ b/resources/lang/zh_CN/store-stock-total.php @@ -0,0 +1,9 @@ + [ + 'store' => '门店管理', + 'StockTotal' => '库存盘点', + 'stock-total' => '库存盘点', + ] +];