feat: #10000 配货功能

This commit is contained in:
赵世界 2023-04-04 15:28:56 +08:00
parent 9ea6d58df7
commit 48888606da
14 changed files with 410 additions and 32 deletions

View File

@ -0,0 +1,85 @@
<?php
namespace App\Exports;
use App\Models\Goods;
use App\Utils\ArrayUtils;
use Maatwebsite\Excel\Concerns\FromCollection;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\WithStyles;
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
class OrderBlankExport implements FromCollection, WithStyles
{
private $data;
private $shopName;
private $no;
private $count;
public function __construct($shopName, $no, $excelDate)
{
$this->shopName = $shopName;
$this->no = $no;
$this->count = count($excelDate);
$this->data = $this->createData($excelDate);
}
/**
* @return \Illuminate\Support\Collection
*/
public function collection()
{
return new Collection($this->data);
}
private function createData($excelDate)
{
$noStr = implode(',', $this->no);
$data = [
['店铺名称', $this->shopName],
// ['跟团号', $noStr],
['种类', '品牌', '商品规格', '商品名称', '数量', '所在货架编号', '配货'],
];
$goodsCodes = array_column($excelDate, 'goods_code');
$goods = Goods::query()
->with([
'brand:id,name',
'type:id,name',
])
->whereIn('goods_code', $goodsCodes)
->get(['goods_code', 'type_id', 'brand_id'])
->toArray();
$goods = ArrayUtils::index($goods, 'goods_code');
foreach ($excelDate as $key => $item) {
$goodsInfo = $goods[$item['goods_code']];
$arr[0] = $goodsInfo['type']['name'];
$arr[1] = $goodsInfo['brand']['name'];
$arr[2] = $key;
$arr[3] = $item['goods_name'];
$arr[4] = $item['num'];
$arr[5] = $item['local'];
$arr[6] = implode(' ', $item['p']);
$data[] = $arr;
}
return $data;
}
public function styles(Worksheet $sheet)
{
$count = $this->count + 2;
$sheet->getStyle('B2')->getAlignment()
->setVertical('center')
->setWrapText(true);
$sheet->getStyle('G3:G' . $count)->getAlignment()
->setVertical('center')
->setWrapText(true);
$sheet->getColumnDimension('A')->setWidth(10);
$sheet->getColumnDimension('B')->setWidth(10);
$sheet->getColumnDimension('C')->setWidth(10);
$sheet->getColumnDimension('D')->setWidth(60);
$sheet->getColumnDimension('E')->setWidth(5);
$sheet->getColumnDimension('F')->setWidth(11);
$sheet->getColumnDimension('G')->setWidth(14);
}
}

View File

@ -2,12 +2,15 @@
namespace App\Http\Controllers\Business;
use App\Exports\OrderBlankExport;
use App\Http\Controllers\Controller;
use App\Models\BusinessOrder;
use App\Models\Shop;
use App\Utils\DateTimeUtils;
use Carbon\Carbon;
use Illuminate\Http\Request;
use App\Http\Resources\BusinessOrderResource;
use Maatwebsite\Excel\Facades\Excel;
class BusinessOrderController extends Controller
{
@ -17,7 +20,7 @@ class BusinessOrderController extends Controller
->where('shop_id', '<>', 8)
->with([
'shop:id,name',
'items:id,business_order_id,goods_name,goods_number'
'items:id,business_order_id,goods_name,goods_number,external_sku_id'
])
->orderByDesc('confirm_at')
->filter()
@ -31,16 +34,16 @@ class BusinessOrderController extends Controller
$shopId = $request->get('shop_id');
$startNo = $request->get('start_no');
$endNo = $request->get('end_no');
$startTime = BusinessOrder::query()->where('participate_no', $startNo)->value('confirm_at');
$endTime = BusinessOrder::query()->where('participate_no', $endNo)->value('confirm_at');
$startTime = BusinessOrder::query()->where('shop_id', $shopId)->where('participate_no', $startNo)->value('confirm_at');
$endTime = BusinessOrder::query()->where('shop_id', $shopId)->where('participate_no', $endNo)->value('confirm_at');
$orders = BusinessOrder::query()
->with([
'items:id,business_order_id,external_sku_id,goods_number,goods_name',
'items.goodsSkuLocation:id,external_sku_id,location'
'items.goodsSkuLocation:id,external_sku_id,location,goods_name'
])
->where('shop_id', $shopId)
->where('confirm_at', '>=', $startTime)
->where('confirm_at', '<=', $endTime)
->where('confirm_at', '>=', DateTimeUtils::getMicroTime($startTime))
->where('confirm_at', '<=', DateTimeUtils::getMicroTime($endTime))
->where('after_sales_status', 0)
->where('cancel_status', 0)
->where('is_supplier', 1)
@ -51,19 +54,32 @@ class BusinessOrderController extends Controller
foreach ($orders as $key => $order) {
$no[] = $order->participate_no;
foreach ($order->items as $item) {
$item = $item->toArray();
if (empty($item['external_sku_id'])) {
continue;
}
$local = $item['goods_sku_location'] ? $item['goods_sku_location']['location'] : '货架未找到';
$index = $key + 1;
$index = 'P' . $index . "({$order->participate_no}) * " . $item['goods_number'];
[$goodsCode, $skuCode] = explode('_', $item['external_sku_id']);
if (isset($distribution[$item['external_sku_id']])) {
$distribution[$item['external_sku_id']]['p'][] = $key;
$distribution[$item['external_sku_id']]['p'][] = $index;
$distribution[$item['external_sku_id']]['num'] += $item['goods_number'];
} else {
$distribution[$item['external_sku_id']] = [
'p' => [$key],
'local' => 0,
'num' => $item['goods_number']
'p' => [$index],
'local' => $local,
'num' => $item['goods_number'],
'goods_name' => $item['goods_name'],
'goods_code' => $goodsCode,
];
}
}
}
var_dump($distribution, $no);
$shopName = Shop::query()->where('id', $shopId)->value('name');
ob_end_clean();
return Excel::download(new OrderBlankExport($shopName, $no, $distribution), $shopName . date('Y-m-d H:i:s') . '.xlsx');
}
public function groupActivity(Request $request, $shopId)
@ -74,8 +90,8 @@ class BusinessOrderController extends Controller
return BusinessOrder::query()
->where('shop_id', $shopId)
->where('confirm_at', '>=', $todayTime)
->groupBy('activity_no')
->pluck('activity_title', 'activity_no')
->groupBy(['activity_no', 'activity_title'])
->get(['activity_title', 'activity_no'])
->toArray();
}
}

View File

@ -67,7 +67,6 @@ class GoodsSkuLocationController extends Controller
$import = new GoodsSkuLocationImport();
$path = $request->file('goodsSkuLocation');
Excel::import($import, $path);
$this->addLog(0, 'import');
} catch (ValidationException $exception) {
$this->setValidatorFailResponse($exception->validator->getMessageBag()->getMessages());
}

View File

@ -28,17 +28,19 @@ class GoodsSkuLocationImport implements ToArray, SkipsEmptyRows, WithStartRow
GoodsSkuLocation::query()->whereIn('location', $deleteLocation)->delete();
unset($row);
$goods = Goods::query()
->with(['skus:id,goods_id,sku_code'])
->whereIn('goods_code', $goodsCode)
->get(['id', 'goods_code']);
->get(['id', 'goods_code', 'title']);
$goodsSkus = [];
foreach ($goods as $goodsItem) {
$goodsSkus[$goodsItem['goods_code']][$goodsItem->skus->sku_code] = [
foreach ($goodsItem->skus as $sku){
$goodsSkus[$goodsItem['goods_code']][$sku->sku_code] = [
'goods_id' => $goodsItem->id,
'goods_sku_id' => $goodsItem->skus->id,
'external_sku_id' => $goodsItem['goods_code'] . '_' . $goodsItem->skus->sku_code,
'goods_sku_id' => $sku->id,
'external_sku_id' => $goodsItem['goods_code'] . '_' . $sku->sku_code,
'goods_name' => $goodsItem['title'] . '_' . $sku->title,
];
}
}
$data = [];
foreach ($array as $row) {
if (isset($goodsSkus[$row[2]][$row[4]])) {

View File

@ -83,9 +83,7 @@ class BusinessOrder extends Model
public function getAfterSalesStatusAttribute($value)
{
$map = ['未售后', '有售后'];
return $map[$value];
return empty($value) ? '未售后' : '有售后';
}
public function items()

View File

@ -40,6 +40,6 @@ class DateTimeUtils
}
$time *= 1000;
return ceil($time);
return (int)ceil($time);
}
}

View File

@ -22,6 +22,7 @@ class CreateGoodsSkuLocationsTable extends Migration
$table->string('location');
$table->integer('today_init_num');
$table->string('external_sku_id');
$table->string('goods_name')->nullable();
$table->tinyInteger('status')->default(1);
$table->string('note')->nullable();
$table->timestamps();

View File

@ -94,3 +94,36 @@ export function getGoodsList(params) {
params,
});
}
// 商品货架
export function goodsSkuLocation(params) {
return http({
url: "/api/goods_sku_location",
method: "get",
params,
});
}
export function importGoodsSkuLocation(params) {
return http({
url: "/api/goods_sku_location",
method: "post",
params,
});
}
export function updateGoodsSkuLocation(params) {
return http({
url: "/api/goods_sku_location",
method: "put",
params,
});
}
export function deleteGoodsSkuLocation(params) {
return http({
url: "/api/goods_sku_location",
method: "delete",
params,
});
}

View File

@ -46,3 +46,11 @@ export function platOrderList(params) {
params
});
}
export function platOrderExport(params) {
return http({
url: "/plat_orders/export",
method: "get",
params,
});
}

View File

@ -26,6 +26,11 @@ const list = [
name: "商品品牌",
component: () => import("../views/brand/brand.vue"),
},
{
path: "GOODS_SKU_LOCATION",
name: "商品货架",
component: () => import("../views/goods/location.vue"),
},
{
path: "SHOP_MANAGE",
name: "店铺管理",

View File

@ -0,0 +1,171 @@
<template>
<div class="conent">
<el-card :body-style="{ padding: '20px 20px 0 20px' }">
<el-form ref="form" :inline="true" :model="form">
<el-form-item label="日期:">
<el-date-picker v-model="form.date" type="date" placeholder="日期"></el-date-picker>
</el-form-item>
<el-form-item label="商品名称:">
<el-input v-model="form.goods_title" placeholder="商品名称"></el-input>
</el-form-item>
<el-form-item label="商品编码:">
<el-input v-model="form.goods_code" placeholder="商品编码"></el-input>
</el-form-item>
<el-form-item label="完整编码:">
<el-input v-model="form.external_sku_id" placeholder="完整编码"></el-input>
</el-form-item>
<el-form-item label="货架号:">
<el-input v-model="form.location" placeholder="货架号"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleChoose">筛选</el-button>
<el-button plain @click="handleReChoose">重置筛选</el-button>
<el-upload ref="myUpload" action="/api/goods_sku_location" :multiple="false" name="goodsSkuLocation"
:show-file-list="false" :on-success="uploadSuccess" :before-upload="beforeUpload"
:on-error="uploadError" style="display:inline-block;margin: 0 10px 0 10px;">
<el-button type="primary" plain>商品货架导入</el-button>
</el-upload>
</el-form-item>
</el-form>
</el-card>
<el-card style="margin-top: 10px">
<el-table v-loading="loading" ref="multipleTable" :data="tableData" style="width: 100%">
<el-table-column prop="date" label="日期"></el-table-column>
<el-table-column label="种类">
<template slot-scope="scope">
{{ scope.row.goods.type.name }}
</template>
</el-table-column>
<el-table-column label="品牌">
<template slot-scope="scope">
{{ scope.row.goods.brand.name }}
</template>
</el-table-column>
<el-table-column label="商品名称" prop="goods_name"></el-table-column>
<el-table-column label="完整编码" prop="external_sku_id"></el-table-column>
<el-table-column label="库存">
<template slot-scope="scope">
{{ scope.row.goods_sku.stock }}
</template>
</el-table-column>
<el-table-column prop="location" label="货架号"></el-table-column>
<el-table-column prop="today_init_num" label="导入时数量"></el-table-column>
<!-- <el-table-column prop="status" label="状态"></el-table-column>
<el-table-column prop="note" label="备注"></el-table-column> -->
</el-table>
<!-- 分页功能 -->
<div class="block">
<el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
:current-page="current_page" :page-sizes="[15, 50, 100]" :page-size="per_page"
layout="total, sizes, prev, pager, next, jumper" :total="Paginationdata.total">
</el-pagination>
</div>
</el-card>
</div>
</template>
<script>
import { goodsSkuLocation } from "../../api/goods";
export default {
data() {
return {
form: {
date: "",
goods_title: "",
goods_code: "",
external_sku_id: "",
location: "",
},
dialogVisible: false,
loading: true,
tableData: [],
Paginationdata: {}, //
current_page: 1, //
per_page: 10, //
};
},
mounted() {
this.goodsSkuLocation();
},
methods: {
//
goodsSkuLocation(params = {}) {
params.page = this.current_page;
params.per_page = this.per_page;
goodsSkuLocation(params).then((res) => {
this.tableData = res.data.data;
this.Paginationdata = res.data.meta;
});
this.loading = false;
},
//
handleSizeChange(val) {
//
this.current_page = 1;
this.per_page = val;
this.goodsSkuLocation();
},
handleCurrentChange(val) {
//
this.current_page = val;
this.goodsSkuLocation();
},
//
handleChoose() {
this.form = {
...this.form,
page: 1,
per_page: this.per_page,
};
this.goodsSkuLocation(this.form);
},
//
handleReChoose() {
this.form = {
shipping_status: "",
is_supplier: "",
cancel_status: "",
after_sales_status: "",
};
this.goodsSkuLocation();
},
beforeUpload() {
this.loadingModule = this.$loading({
lock: true,
text: '盘点导入中...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
},
uploadSuccess(response) {
this.$message({
message: response.message,
type: "success",
});
this.loadingModule.close();
},
uploadError(err) {
this.$message({
message: err.errorMessage,
type: "error",
});
this.loadingModule.close();
}
},
};
</script>
<style lang="scss" scoped>
.block {
margin-top: 20px;
}
</style>

View File

@ -3,7 +3,7 @@
<el-card :body-style="{ padding: '20px 20px 0 20px' }">
<el-form ref="form" :inline="true" :model="form">
<el-form-item label="店铺:">
<el-select v-model="form.shop_id" placeholder="店铺">
<el-select v-model="form.shop_id" placeholder="店铺" @change="setActivity">
<el-option v-for="item in shops" :key="item.id" :label="item.name" :value="item.id">
</el-option>
</el-select>
@ -51,9 +51,10 @@
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleChoose()">筛选</el-button>
<el-button plain @click="handleReChoose()">重置筛选</el-button>
<el-button type="primary" @click="handleChoose">筛选</el-button>
<el-button plain @click="handleReChoose">重置筛选</el-button>
</el-form-item>
<el-button type="primary" @click="dialogVisible = true">配货单导出</el-button>
</el-form>
</el-card>
@ -72,7 +73,9 @@
<el-table-column prop="participate_no" label="跟团号"></el-table-column>
<el-table-column label="商品信息">
<template slot-scope="scope">
<div v-for="item in scope.row.items" :key="item.id">{{ item.goods_name }}*{{ item.goods_number }}
<div v-for="item in scope.row.items" :key="item.id">
<!-- {{ item.goods_name }}*{{ item.goods_number }} -->
{{ item.external_sku_id }}
</div>
</template>
</el-table-column>
@ -87,11 +90,33 @@
</el-pagination>
</div>
</el-card>
<!-- 配货单导出 -->
<el-dialog title="配货单导出" :visible.sync="dialogVisible" :close-on-click-modal="false">
<el-form ref="exportForm" :model="exportForm" label-width="100px" :rules="exportFormRules">
<el-form-item label="店铺:" prop="shop_id">
<el-select v-model="exportForm.shop_id" placeholder="店铺" @change="setActivity">
<el-option v-for="item in shops" :key="item.id" :label="item.name" :value="item.id">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="开始跟团号" prop="start_no">
<el-input v-model="exportForm.start_no"></el-input>
</el-form-item>
<el-form-item label="结束跟团号" prop="end_no">
<el-input v-model="exportForm.end_no"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="derivation('exportForm')">导出</el-button>
<el-button @click="dialogVisible = false">取消</el-button>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script>
import { platOrderList, activityList } from "../../api/plat";
import { platOrderList, activityList, platOrderExport } from "../../api/plat";
import { storeList } from "../../api/shop";
export default {
data() {
@ -104,6 +129,7 @@ export default {
cancel_status: "",
after_sales_status: "",
},
dialogVisible: false,
loading: true,
tableData: [],
Paginationdata: {}, //
@ -111,6 +137,22 @@ export default {
per_page: 10, //
shops: [],
groupActivity: [],
exportForm: {
shop_id: "",
start_no: "",
end_no: ""
},
exportFormRules: {
shop_id: [
{ required: true, trigger: 'blur' }
],
start_no: [
{ required: true, trigger: 'blur' }
],
end_no: [
{ required: true, trigger: 'blur' }
],
}
};
},
mounted() {
@ -173,6 +215,22 @@ export default {
};
this.getPlatOrderList();
},
setActivity(shopId) {
activityList(shopId).then((res) => {
this.groupActivity = res.data;
})
},
derivation(formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
window.open("/plat_orders/export?shop_id=" + this.exportForm.shop_id + '&start_no=' + this.exportForm.start_no + '&end_no=' + this.exportForm.end_no);
} else {
return false;
}
});
}
},
};
</script>

View File

@ -49,14 +49,12 @@ Route::middleware(['auth:api', 'check.permissions'])->group(function () {
// 平台
Route::resource('plat_goods', 'Business\BusinessGoodsSkusController', ['only' => ['index', 'update', 'destroy']]);
Route::get('plat_orders', [BusinessOrderController::class, 'index'])->name('plat.orders.index');
Route::get('plat_orders/export', [BusinessOrderController::class, 'exportOrderBlank'])->name('plat.orders.export');
Route::get('plat_group_activity/{shopId}', [BusinessOrderController::class, 'groupActivity'])->name('plat.activity.list');
Route::post('plat/sync/{id}/stock', [BusinessGoodsSkusController::class, 'syncStock'])->name('plat.sync.stock');
// 团购
Route::resource('group', 'Group\GroupsController', ['only' => ['index', 'store', 'show', 'update', 'destroy']]);
// 商品货架
Route::get('goods_sku_location', [GoodsSkuLocationController::class, 'index'])->name('goods_sku_location.index');
Route::post('goods_sku_location', [GoodsSkuLocationController::class, 'import'])->name('goods_sku_location.import');
Route::put('goods_sku_location', [GoodsSkuLocationController::class, 'update'])->name('goods_sku_location.update');
Route::delete('goods_sku_location', [GoodsSkuLocationController::class, 'delete'])->name('goods_sku_location.delete');
});
@ -82,5 +80,7 @@ Route::post('business', [ShopsController::class, 'business'])->name('shop.put.bu
// 盘点导入
Route::post('inventory/goods_skus', [GoodsSkusController::class, 'inventoryImport'])->name('goods_sku.inventory');
// 商品货架导入
Route::post('goods_sku_location', [GoodsSkuLocationController::class, 'import'])->name('goods_sku_location.import');
Route::post('upload', [UploadController::class, 'store'])->name('upload.file');

View File

@ -3,6 +3,7 @@
use App\Http\Controllers\Goods\GoodsSkusController;
use App\Http\Controllers\Goods\GoodsController;
use App\Http\Controllers\Shop\ShopsController;
use App\Http\Controllers\Business\BusinessOrderController;
/*
|--------------------------------------------------------------------------
@ -20,6 +21,7 @@ Route::get('/', function () {
});
Route::get('goods_skus/export', [GoodsSkusController::class, 'export'])->name('goods_skus.export');
Route::get('plat_orders/export', [BusinessOrderController::class, 'exportOrderBlank'])->name('plat.orders.export');
Route::get('goods/import/template', [GoodsController::class, 'download'])->name('download.goods_import.template');