diff --git a/app/Admin/Controllers/Store/ProductController.php b/app/Admin/Controllers/Store/ProductController.php
index 3eccdf38..d3d75c40 100644
--- a/app/Admin/Controllers/Store/ProductController.php
+++ b/app/Admin/Controllers/Store/ProductController.php
@@ -56,20 +56,20 @@ class ProductController extends AdminController
$grid->column('productSku.specs', '规格')->label();
$grid->column('productSku.sell_price', '销售价')->display(fn ($value) => bcdiv($value, 100, 2));
$grid->column('productSku.cost_price', '成本价')->display(fn ($value) => bcdiv($value, 100, 2));
- $grid->column('profit', '毛利')->display(function () {
- $sell_price = data_get($this->productSku, 'sell_price');
- $cost_price = data_get($this->productSku, 'cost_price');
- return round(($sell_price - $cost_price) / 100, 2, PHP_ROUND_HALF_DOWN);
- });
+ // $grid->column('profit', '毛利')->display(function () {
+ // $sell_price = data_get($this->productSku, 'sell_price');
+ // $cost_price = data_get($this->productSku, 'cost_price');
+ // return round(($sell_price - $cost_price) / 100, 2, PHP_ROUND_HALF_DOWN);
+ // });
$grid->column('amount', '库存');
- $grid->column('sell', '总销售')->display(function () {
- $sell_price = data_get($this->productSku, 'sell_price');
- return round($sell_price / 100 * $this->amount, 2, PHP_ROUND_HALF_DOWN);
- });
- $grid->column('cost', '总成本')->display(function () {
- $cost_price = data_get($this->productSku, 'cost_price');
- return round($cost_price / 100 * $this->amount, 2, PHP_ROUND_HALF_DOWN);
- });
+ // $grid->column('sell', '总销售')->display(function () {
+ // $sell_price = data_get($this->productSku, 'sell_price');
+ // return round($sell_price / 100 * $this->amount, 2, PHP_ROUND_HALF_DOWN);
+ // });
+ // $grid->column('cost', '总成本')->display(function () {
+ // $cost_price = data_get($this->productSku, 'cost_price');
+ // return round($cost_price / 100 * $this->amount, 2, PHP_ROUND_HALF_DOWN);
+ // });
$grid->column('status', '状态')->switch();
$grid->paginate(10);
@@ -92,33 +92,30 @@ class ProductController extends AdminController
$tools->append(new \App\Admin\Actions\Store\DownloadProductTemplate());
});
- $grid->footer(function ($collection) use ($grid) {
- $query = StoreProductSku::join('product_skus', 'product_skus.id', '=', 'store_product_skus.product_sku_id');
- $grid->model()->getQueries()->unique()->each(function ($value) use (&$query) {
- if (in_array($value['method'], ['paginate', 'get', 'orderBy', 'orderByDesc'], true)) {
- return;
- }
+ // $grid->footer(function ($collection) use ($grid) {
+ // $query = StoreProductSku::join('product_skus', 'product_skus.id', '=', 'store_product_skus.product_sku_id');
+ // $grid->model()->getQueries()->unique()->each(function ($value) use (&$query) {
+ // if (in_array($value['method'], ['paginate', 'get', 'orderBy', 'orderByDesc'], true)) {
+ // return;
+ // }
- $query = call_user_func_array([$query, $value['method']], $value['arguments'] ?? []);
- });
- $count = $query->count();
- $stock = round($query->sum('store_product_skus.amount'), 2, PHP_ROUND_HALF_DOWN);
- $cost = round($query->sum(DB::raw('product_skus.cost_price * amount')) / 100, 2, PHP_ROUND_HALF_DOWN);
- $sell = round($query->sum(DB::raw('product_skus.sell_price * amount')) / 100, 2, PHP_ROUND_HALF_DOWN);
- return <<
-
-
- | 统计 |
- 总数: $count |
- 库存: $stock |
- 总销售: $sell |
- 总成本: $cost |
-
-
-
- HTML;
- });
+ // $query = call_user_func_array([$query, $value['method']], $value['arguments'] ?? []);
+ // });
+ // $count = $query->count();
+ // return <<
+ //
+ //
+ // | 统计 |
+ // 总数: $count |
+ // |
+ // |
+ // |
+ //
+ //
+ //
+ // HTML;
+ // });
return $grid;
}
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 .= '入库总成本: '.data_get($total, 'total_in').' | ';
+ $html .= '销售总金额: '.data_get($total, 'total_sell').' | ';
+ $html .= '线上销售总金额: '.data_get($total, 'total_online').' | ';
+ $html .= '报损总成本: '.data_get($total, 'total_loss').' | ';
+ $html .= '自用总成本: '.data_get($total, 'total_self').' | ';
+ $html .= '调货总金额: '.data_get($total, 'total_out').' | ';
+ $html .= '现有总成本: '.data_get($total, 'total_cost_price').' | ';
+ $html .= '现有总价值: '.data_get($total, 'total_sell_price').' | ';
+ $html .= '产品毛利汇总: '.data_get($total, 'total_profit').' | ';
+ $html .= '
';
+ return $html;
+ });
+ });
+ }
+}
diff --git a/app/Admin/Forms/OrderPackage.php b/app/Admin/Forms/OrderPackage.php
index d12b9b6f..e4348fdf 100644
--- a/app/Admin/Forms/OrderPackage.php
+++ b/app/Admin/Forms/OrderPackage.php
@@ -11,6 +11,8 @@ use Dcat\Admin\Widgets\Form;
use Illuminate\Support\Facades\DB;
use Throwable;
use Dcat\Admin\Admin;
+use App\Models\Store\ProductSku as StoreProductSku;
+use App\Models\Store\Store;
class OrderPackage extends Form implements LazyRenderable
{
@@ -45,10 +47,11 @@ class OrderPackage extends Form implements LazyRenderable
$orderPackageService = new OrderPackageService();
$orderPackageService->createPackage($order, $input);
// 店铺发货, 添加出库记录
- if ($order->store) {
+ $store_id = data_get($input, 'store_id', $order->store_id);
+ if ($store_id) {
$packageProducts = $input['packages'];
$operator = Admin::user();
- $store = $order->store;
+ $store = Store::findOrFail($store_id);
$tag = Tag::firstOrCreate([
'type' => Tag::TYPE_STORE_STOCK,
'name' => '发货'
@@ -114,6 +117,10 @@ class OrderPackage extends Form implements LazyRenderable
'提货' => '提货',
])->default('韵达快递')->required();
$this->text('shipping_number')->required();
+
+ $list = StoreProductSku::with(['store'])->whereIn('product_sku_id', $order->products->pluck('sku_id'))->get();
+ $stores = $list->pluck('store');
+ $this->select('store_id', '发货门店')->options($stores->pluck('title', 'id'))->value($order->store_id);
$products = $order->products->filter(function ($items) {
if ($items->after_sale_state != 1) {
return true;
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 434adaf0..f0579727 100644
--- a/app/Admin/routes.php
+++ b/app/Admin/routes.php
@@ -194,6 +194,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/database/sql/tags.sql b/database/sql/tags.sql
new file mode 100644
index 00000000..53ef03d0
--- /dev/null
+++ b/database/sql/tags.sql
@@ -0,0 +1,9 @@
+delete from `tags` where `type` = 4;
+INSERT INTO `tags` (`id`, `type`, `name`, `created_at`, `updated_at`) VALUES
+(1, 4, '调出', '2022-06-12 19:49:55', '2023-08-10 04:24:02'),
+(2, 4, '进货', '2022-06-12 19:49:55', '2023-08-10 04:23:31'),
+(3, 4, '提货', '2022-07-27 01:05:41', '2022-07-27 01:05:41'),
+(4, 4, '报损', '2022-08-14 11:22:07', '2022-08-14 11:22:07'),
+(5, 4, '自用', '2022-08-14 11:24:40', '2022-08-14 11:24:55'),
+(9, 4, '发货', '2022-08-26 01:54:33', '2022-08-26 01:54:33'),
+(13, 4, '调入', '2023-08-10 04:24:14', '2023-08-10 04:24:14');
\ No newline at end of file
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' => '库存盘点',
+ ]
+];