diff --git a/app/Admin/Controllers/ProductSkuController.php b/app/Admin/Controllers/ProductSkuController.php index 4b59b23c..dfa356f8 100644 --- a/app/Admin/Controllers/ProductSkuController.php +++ b/app/Admin/Controllers/ProductSkuController.php @@ -14,6 +14,7 @@ use Dcat\Admin\Grid; use Dcat\Admin\Http\Controllers\AdminController; use Dcat\Admin\Show; use Illuminate\Http\Request; +use Illuminate\Support\Str; class ProductSkuController extends AdminController { @@ -135,6 +136,13 @@ class ProductSkuController extends AdminController $form->select('attr_group')->options(ProductGroup::all()->pluck('name', 'id')); $form->selectAttr('attrs')->listen('attr_group'); $form->hidden('verify_state'); + + $form->ignore(['attr_group']); + $form->display('created_at'); + $form->display('updated_at'); + + $form->hidden('is_pre_sale')->default(0); + $form->saving(function (Form $form) { if (!(is_null($form->model()->release_at) || $form->model()->verify_state == 1)) { return $form->response()->error('当前sku商品无法修改'); @@ -142,11 +150,9 @@ class ProductSkuController extends AdminController $form->verify_state = 0; }); - $form->ignore(['attr_group']); - $form->display('created_at'); - $form->display('updated_at'); - - $form->hidden('is_pre_sale')->default(0); + $form->disableEditingCheck(); + $form->disableCreatingCheck(); + $form->disableDeleteButton(); }); } @@ -179,4 +185,14 @@ class ProductSkuController extends AdminController }) ]); } + + public function update($id) + { + $last = url()->previous(); + $redirect = null; + if (Str::contains($last, 'product-spus')) { + $redirect = admin_route('product_spus.sku_list', ['spu' => ProductSkuModel::where('id', $id)->value('spu_id')]); + } + return $this->form()->update($id, request()->all(), $redirect); + } } diff --git a/app/Admin/Controllers/ProductSpuController.php b/app/Admin/Controllers/ProductSpuController.php index 97d76d84..3b92c65f 100644 --- a/app/Admin/Controllers/ProductSpuController.php +++ b/app/Admin/Controllers/ProductSpuController.php @@ -229,12 +229,7 @@ class ProductSpuController extends AdminController return parent::destroy($id); } - - /** - * Undocumented function - * - * @return void - */ + public function skuList(Content $content, ProductSpuModel $spu) { return $content->header(__('product-spu.labels.ProductSpu')) diff --git a/app/Admin/Controllers/Store/OrderController.php b/app/Admin/Controllers/Store/OrderController.php index b865b014..0a9d871f 100644 --- a/app/Admin/Controllers/Store/OrderController.php +++ b/app/Admin/Controllers/Store/OrderController.php @@ -9,7 +9,7 @@ use App\Enums\PayWay; use Dcat\Admin\Layout\Row; use Dcat\Admin\Widgets\{Box, Tab, Card}; use App\Constants\OrderStatus; -use App\Models\Store\Store; +use App\Models\Store\{Store, Desk}; class OrderController extends AdminController { @@ -33,7 +33,7 @@ class OrderController extends AdminController protected function grid() { - $grid = new Grid(Order::with(['user', 'userInfo', 'inviter', 'inviterInfo', 'store'])); + $grid = new Grid(Order::with(['user', 'userInfo', 'inviter', 'inviterInfo', 'store', 'userCoupon'])); $user = Admin::user(); $canAdmin = $this->canAdmin(); @@ -70,6 +70,7 @@ class OrderController extends AdminController $grid->column('market_price')->display(fn ($value) => bcdiv($value, 100, 2))->prepend('¥'); $grid->column('cost_price')->display(fn ($value) => bcdiv($value, 100, 2))->prepend('¥'); $grid->column('profit_price')->display(fn () => round(($this->total_amount - $this->cost_price) / 100, 2, PHP_ROUND_HALF_DOWN)); + $grid->column('user_coupon_id')->display(fn ($value) => $value ? data_get($this->userCoupon, 'coupon_name') . '(¥'.round($this->coupon_discount_amount/100, 2, PHP_ROUND_HALF_DOWN).')' : ''); $grid->column('sales_value'); $grid->column('order_status')->using($this->statusMap)->dot($this->statusColor); $grid->column('created_at'); @@ -108,6 +109,7 @@ class OrderController extends AdminController // PayWay::WxpayMiniProgram->value => PayWay::WxpayMiniProgram->text(), // PayWay::Offline->value => PayWay::Offline->text(), // ])->width(3); + $filter->between('created_at')->dateTime()->width(6); $filter->where('order_status', function ($q) { switch ($this->input) { case OrderStatus::PENDING: @@ -137,8 +139,9 @@ class OrderController extends AdminController OrderStatus::COMPLETED => '已完成', OrderStatus::CANCELLED => '已取消' ])->width(3); - $filter->between('created_at')->dateTime()->width(6); + $filter->where('user_coupon_id', fn($q) => $this->input ? $q->whereNotNull('user_coupon_id') : $q->whereNull('user_coupon_id'))->select(['未使用', '已使用'])->width(3); $filter->between('completed_at')->dateTime()->width(6); + $filter->where('pay_at', fn($q) => $this->input ? $q->whereNotNull('pay_at') : $q->whereNull('pay_at')->where('status', '!=', Order::STATUS_CANCELLED), '付款状态')->select(['待付款', '已付款'])->width(3); }); $grid->footer(function ($collection) use ($grid) { @@ -175,35 +178,35 @@ class OrderController extends AdminController protected function detail($id) { - $show = Show::make($id, Order::with(['user', 'inviter'])); + $show = Show::make($id, Order::with(['user', 'inviter', 'userCoupon', 'source'])); + $model = $show->model(); $show->field('id'); $show->field('sn'); $show->field('user.phone'); $show->field('inviter.phone'); $show->field('products_total_amount')->as(fn($value) => bcdiv($value, 100, 2))->prepend('¥'); - $show->field('total_amount')->as(fn ($value) => bcdiv($value, 100, 2)); $show->field('market_price')->as(fn ($value) => bcdiv($value, 100, 2)); $show->field('cost_price')->as(fn ($value) => bcdiv($value, 100, 2)); $show->field('profit_price')->as(fn () => round(($this->total_amount - $this->cost_price) / 100, 2, PHP_ROUND_HALF_DOWN)); $show->field('vip_discount_amount')->as(fn ($v) => bcdiv($v, 100, 2))->prepend('- ¥'); + + // 优惠券 + $show->field('user_coupon_id')->as(fn() => data_get($this->userCoupon, 'coupon_name')); + $show->field('coupon_discount_amount')->as(fn($value) => bcdiv($value, 100, 2))->prepend('- ¥'); - $userCouponId = $show->model()->user_coupon_id; - if ($userCouponId) { - $show->field('coupon_discount_amount')->as(fn($value) => bcdiv($value, 100, 2))->prepend('- ¥'); - } $show->field('shipping_fee')->as(fn ($v) => bcdiv($v, 100, 2))->prepend('+ ¥'); $show->field('reduced_amount')->as(fn($value) => bcdiv($value, 100, 2))->prepend('- ¥'); - $show->field('total_amount', '支付金额')->as(fn($value) => bcdiv($value, 100, 2))->prepend('¥'); + $show->field('total_amount')->as(fn($value) => bcdiv($value, 100, 2))->prepend('¥'); $show->field('sales_value'); - $show->field('order_status')->as(function ($v) { - return $this->order_status; - })->using($this->statusMap)->dot($this->statusColor); - $show->field('pay_way')->as(function ($v) { - return $this->pay_way?->mallText(); - })->circleDot(PayWay::colors()); + $show->field('order_status')->as(fn() => $this->order_status)->using($this->statusMap)->dot($this->statusColor); + $show->field('pay_way')->as(fn() => $this->pay_way?->mallText())->circleDot(PayWay::colors()); $show->field('created_at'); $show->field('pay_at'); $show->field('completed_at'); + + if ($model->source_type == Desk::class) { + $show->field('desk_id')->as(fn() => data_get($model->source, 'name')); + } $show->panel()->tools(function (Show\Tools $tools) { $tools->disableEdit(); diff --git a/app/Admin/Controllers/Store/StockTotalController.php b/app/Admin/Controllers/Store/StockTotalController.php index f0724476..9d1cd2d8 100644 --- a/app/Admin/Controllers/Store/StockTotalController.php +++ b/app/Admin/Controllers/Store/StockTotalController.php @@ -47,6 +47,8 @@ class StockTotalController extends AdminController $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->equal('tag_id', '类目')->select(StockLog::tags()->pluck('name', 'id'))->width(3); + // $filter->in('tag_id', '类目')->multipleSelect(StockLog::tags()->pluck('name', 'id'))->width(3); $filter->between('date', '时间')->date()->width(6); }); diff --git a/app/Admin/Repositories/StoreTockRepository.php b/app/Admin/Repositories/StoreTockRepository.php index 6b014d68..9e937b78 100644 --- a/app/Admin/Repositories/StoreTockRepository.php +++ b/app/Admin/Repositories/StoreTockRepository.php @@ -7,6 +7,7 @@ use App\Models\Store\{StockLog, ProductSku}; use Dcat\Admin\Repositories\Repository; use Illuminate\Support\Facades\DB; use Carbon\Carbon; +use Illuminate\Support\Arr; class StoreTockRepository extends Repository { @@ -30,6 +31,7 @@ class StoreTockRepository extends Repository $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; + $current_stock = data_get($item, 'amount'); $subData = [ 'id' => $item->product_sku_id, 'store_name' => data_get($item->store, 'title'), @@ -38,7 +40,7 @@ class StoreTockRepository extends Repository 'category_name' => data_get($item->productSku, 'category.name'), 'cost_price' => $cost_price, 'sell_price' => $sell_price, - 'stock' => data_get($item, 'amount'), + 'stock' => $current_stock, // 入库数 'tag_in' => $in, // 销售数 = 提货数 + 发货数 @@ -53,8 +55,8 @@ class StoreTockRepository extends Repository 'init_stock' => data_get($item->stockLog, 'balance', 0), // 调货数 = 出库数 '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)), + // 毛利 = 现有库存 x (售价-成本) + 'total_profit' => round($current_stock * ($sell_price - $cost_price), PHP_ROUND_HALF_DOWN), ]; array_push($data, $subData); } @@ -69,17 +71,25 @@ class StoreTockRepository extends Repository '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')) { + $filter = $model->filter(); + if ($start = $filter->input('date.start')) { $start = Carbon::createFromFormat('Y-m-d', $start)->startOfDay(); $q->where('created_at', '>=', $start); } - if ($end = $model->filter()->input('date.end')) { + if ($end = $filter->input('date.end')) { $end = Carbon::createFromFormat('Y-m-d', $end)->endOfDay(); $q->where('created_at', '<=', $end); } - if ($store_id = $model->filter()->input('store_id')) { + if ($store_id = $filter->input('store_id')) { $q->where('store_id', $store_id); } + // $tag_ids = data_get($filter->filters(), 3); + // if ($tag_ids && $tag_ids->getValue()) { + // $q->whereIn('tag_id', $tag_ids->getValue()); + // } + if ($tag_id = $filter->input('tag_id')) { + $q->where('tag_id', $tag_id); + } }, 'stockLog' => function ($q) use ($model) { if ($store_id = $model->filter()->input('store_id')) { @@ -134,7 +144,7 @@ class StoreTockRepository extends Repository 'total_cost_price' => 0, // 现有总价值 = 库存 * 售价 'total_sell_price' => 0, - // 毛利 = (入库数-报损数-自用数) x (售价-成本) + // 毛利 = 库存 x (售价-成本) 'total_profit' => 0, ]; foreach ($list as $item) { @@ -154,7 +164,7 @@ class StoreTockRepository extends Repository $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); + $data['total_profit'] += $current_stock * ($sell_price - $cost_price); } return $data; } diff --git a/app/Admin/routes.php b/app/Admin/routes.php index cc703fe2..f0579727 100644 --- a/app/Admin/routes.php +++ b/app/Admin/routes.php @@ -56,9 +56,7 @@ Route::group([ $router->resource('product-spus', 'ProductSpuController')->names('product_spus'); $router->get('product-spus/{spu}/sku-list', 'ProductSpuController@skuList')->name('product_spus.sku_list'); - $router->resource('product-skus', 'ProductSkuController')->only([ - 'index', 'edit', 'update', 'destroy', - ])->names('product_skus'); + $router->resource('product-skus', 'ProductSkuController')->only(['index', 'edit', 'update', 'destroy'])->names('product_skus'); $router->resource('product-sku-verifies', 'ProductSkuVerifyController')->only([ 'index', 'edit', 'update', 'destroy', diff --git a/app/Endpoint/Api/Http/Controllers/AliOssController.php b/app/Endpoint/Api/Http/Controllers/AliOssController.php index 50a6691b..65d33a62 100644 --- a/app/Endpoint/Api/Http/Controllers/AliOssController.php +++ b/app/Endpoint/Api/Http/Controllers/AliOssController.php @@ -20,4 +20,21 @@ class AliOssController extends Controller 'domain'=>config('filesystems.disks.aliyun.domain'), ], $aliStsService->createSts($request->user()?->phone))); } + + public function signature(Request $request, AliStsService $aliStsService) + { + $request->validate([ + 'filename' => 'required' + ]); + $path = $request->input('path', 'uploads').'/'.date('Y-m-d'); + + $data = [ + "accessid" => "", + "host" => "https://post-test.oss-cn-hangzhou.aliyuncs.com", + "policy" => "eyJleHBpcmF0aW9uIjoiMjAxNS0xMS0wNVQyMDoyMzoyM1oiLCJjxb25kaXRpb25zIjpbWyJjcb250ZW50LWxlbmd0aC1yYW5nZSIsMCwxMDQ4NTc2MDAwXSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInVzZXItZGlyXC8i****", + "signature" => "VsxOcOudx******z93CLaXPz+4s=", + "expire" => 1446727949, + "dir" => $path.'/'.$request->input('filename'), + ]; + } } diff --git a/app/Endpoint/Api/Http/Controllers/Product/ProductSkuController.php b/app/Endpoint/Api/Http/Controllers/Product/ProductSkuController.php index 0a052284..7986694a 100644 --- a/app/Endpoint/Api/Http/Controllers/Product/ProductSkuController.php +++ b/app/Endpoint/Api/Http/Controllers/Product/ProductSkuController.php @@ -66,7 +66,11 @@ class ProductSkuController extends Controller $user = $request->user(); $spu = ProductSpu::with(['specs', 'features'])->findOrFail($id); - $sku = $spu->skus()->with(['buynote', 'spu'])->online()->first(); + $sku = $spu->skus() + ->with(['buynote', 'spu']) + ->online() + ->when($request->filled('sku_id'), fn($q) => $q->where('id', $request->input('sku_id'))) + ->first(); if (!$sku) { throw new BizException('商品sku未上架'); } @@ -96,7 +100,7 @@ class ProductSkuController extends Controller $spuSpecItems[] = [ 'name' => $value['name'], 'selected' => $sku->is($mapSku), - 'sku_id' => (int) $mapSku?->id, + 'sku_id' => (int) $mapSku->id, 'sku_stock' => (int) $mapSku?->saleable_stock, ]; } diff --git a/app/Endpoint/Api/Http/Controllers/StoreController.php b/app/Endpoint/Api/Http/Controllers/StoreController.php index a1328f7e..574ed45b 100644 --- a/app/Endpoint/Api/Http/Controllers/StoreController.php +++ b/app/Endpoint/Api/Http/Controllers/StoreController.php @@ -12,6 +12,7 @@ use App\Endpoint\Api\Http\Resources\ProductFeatureResource; use App\Events\ProductSkuViewed; use App\Models\Store\Desk; use App\Endpoint\Api\Http\Resources\DeskResource; +use App\Exceptions\BizException; class StoreController extends Controller { @@ -44,7 +45,7 @@ class StoreController extends Controller $input['sort'] = '-id'; } - $spuIds = StoreProductSku::where('store_id', $store->id)->where('status', 1)->pluck('product_spu_id')->toArray(); + $spuIds = StoreProductSku::where('store_id', $store->id)->where('amount', '>', 0)->where('status', 1)->pluck('product_spu_id')->toArray(); $spuIds = array_unique($spuIds); $spuIds = array_values($spuIds); $list = ProductSpu::with(['specs'])->whereIn('id', $spuIds)->filter($input)->simplePaginate(Paginator::resolvePerPage('per_page', 20, 50)); @@ -66,7 +67,11 @@ class StoreController extends Controller $user = $request->user(); $spu = $store->productSpus()->with(['specs', 'features'])->findOrFail($id); - $sku = $store->productSkus()->where('product_spu_id', $spu->id)->first(); + $sku = $store->productSkus() + ->where('product_spu_id', $spu->id) + ->where('amount', '>', 0) + ->when($request->filled('sku_id'), fn($q) => $q->where('product_sku_id', $request->input('sku_id'))) + ->first(); if (!$sku) { throw new BizException('商品sku未上架'); } @@ -74,7 +79,7 @@ class StoreController extends Controller $spuSpecs = []; if (count($original = (array) $sku->specs) > 0) { - $skus = $spu->skus()->get(['id', 'specs', 'stock', 'release_at']); + $skus = $store->productSkus()->where('product_spu_id', $spu->id)->where('status', 1)->get(); $mapSkus = $skus->mapWithKeys(function ($item) { $key = implode('_', $item->specs) ?: $item->id; @@ -92,12 +97,13 @@ class StoreController extends Controller $key = implode('_', $jSpecs); $mapSku = $mapSkus->get($key); - if ($mapSku) { + $stock = (int) data_get($mapSku?->pivot, 'amount'); + if ($mapSku && $stock > 0) { $spuSpecItems[] = [ 'name' => $value['name'], 'selected' => $sku->is($mapSku), - 'sku_id' => (int) $mapSku?->id, - 'sku_stock' => (int) $mapSku?->saleable_stock, + 'sku_id' => (int) $mapSku->id, + 'sku_stock' => $stock, ]; } diff --git a/app/Services/AliStsService.php b/app/Services/AliStsService.php index 0f07d0b6..00bf5758 100644 --- a/app/Services/AliStsService.php +++ b/app/Services/AliStsService.php @@ -66,4 +66,18 @@ class AliStsService } return []; } + + public function signatureUpload($file) + { + $config = config('filesystems.disks.aliyun'); + $host = (data_get($config, 'use_ssl') ? 'https://' : 'http://') . data_get($config, 'bucket') . '.' . data_get($config, 'endpoint'); + $data = [ + "accessid" => $this->accessId, + "host" => $host, + "policy" => "eyJleHBpcmF0aW9uIjoiMjAxNS0xMS0wNVQyMDoyMzoyM1oiLCJjxb25kaXRpb25zIjpbWyJjcb250ZW50LWxlbmd0aC1yYW5nZSIsMCwxMDQ4NTc2MDAwXSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInVzZXItZGlyXC8i****", + "signature" => "VsxOcOudx******z93CLaXPz+4s=", + "expire" => 1446727949, + "dir" => $file, + ]; + } } diff --git a/resources/lang/zh_CN/store-order.php b/resources/lang/zh_CN/store-order.php index 95ca9da9..4a707d55 100644 --- a/resources/lang/zh_CN/store-order.php +++ b/resources/lang/zh_CN/store-order.php @@ -61,6 +61,7 @@ return [ 'market_price' => '市场价', 'cost_price' => '成本价', 'profit_price' => '毛利', + 'desk_id' => '桌号', ], 'options' => [ ],